PageRenderTime 5ms CodeModel.GetById 9ms app.highlight 120ms RepoModel.GetById 2ms app.codeStats 0ms

/src/griditems/neurogriditem.cpp

https://bitbucket.org/kulibali/neurocogling
C++ | 1411 lines | 1116 code | 219 blank | 76 comment | 175 complexity | e3ac2a95bcdb555969e87d082639989d MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2Neurocognitive Linguistics Lab
   3Copyright (c) 2010,2011 Gordon Tisher
   4All rights reserved.
   5
   6Redistribution and use in source and binary forms, with or without
   7modification, are permitted provided that the following conditions
   8are met:
   9
  10 - Redistributions of source code must retain the above copyright
  11   notice, this list of conditions and the following disclaimer.
  12
  13 - Redistributions in binary form must reproduce the above copyright
  14   notice, this list of conditions and the following disclaimer in
  15   the documentation and/or other materials provided with the
  16   distribution.
  17
  18 - Neither the name of the Neurocognitive Linguistics Lab nor the
  19   names of its contributors may be used to endorse or promote
  20   products derived from this software without specific prior
  21   written permission.
  22
  23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  24"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  25LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  26FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  27COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  28INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  29BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  30LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  31CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  33ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  34POSSIBILITY OF SUCH DAMAGE.
  35*/
  36
  37#include "neurogriditem.h"
  38#include "../neurogui/labnetwork.h"
  39#include "../neurogui/labscene.h"
  40#include "../neurogui/labview.h"
  41#include "../neurogui/labtree.h"
  42#include "../neurogui/mainwindow.h"
  43#include "../neurogui/narrow/neurolinkitem.h"
  44#include "gridedgeitem.h"
  45#include "multigridioitem.h"
  46
  47#include <QtAlgorithms>
  48
  49#include <cmath>
  50#include <cfloat>
  51
  52using namespace NeuroGui;
  53
  54namespace GridItems
  55{
  56
  57    const QString & VERSION()
  58    {
  59        static const QString
  60#include "../version.txt"
  61        ;
  62
  63        return VERSION;
  64    }
  65
  66    NEUROITEM_DEFINE_PLUGIN_CREATOR(NeuroGridItem, QString("Grid Items"), QObject::tr("Grid Item"), ":/griditems/icons/grid_item.png", GridItems::VERSION())
  67
  68    NeuroGridItem::NeuroGridItem(LabNetwork *network, const QPointF & scenePos, const CreateContext & context)
  69        : SubNetworkItem(network, scenePos, context),
  70          _horizontal_property(this, &NeuroGridItem::horizontalCols, &NeuroGridItem::setHorizontalCols, tr("Width")),
  71          _vertical_property(this, &NeuroGridItem::verticalRows, &NeuroGridItem::setVerticalRows, tr("Height")),
  72          _num_horiz(1), _num_vert(1), _connections_changed(true), _pattern_changed(true)
  73    {
  74        if (context == NeuroItem::CREATE_UI)
  75        {
  76            const int width = NODE_WIDTH*4;
  77            const int height = NODE_WIDTH*2;
  78            const int left = -width/2;
  79            const int top = -height/2;
  80
  81            setRect(QRectF(left, top, width, height));
  82            setLabelPos(QPointF(rect().left() + rect().width() + 5, 0));
  83            treeNode()->setLabel(tr("Grid Item %1").arg(treeNode()->id()));
  84        }
  85
  86        connect(MainWindow::instance(), SIGNAL(itemCreated(NeuroItem*)), this, SLOT(itemChanged(NeuroItem*)));
  87        connect(MainWindow::instance(), SIGNAL(itemChanged(NeuroItem*)), this, SLOT(itemChanged(NeuroItem*)));
  88        connect(MainWindow::instance(), SIGNAL(itemDeleted(NeuroItem*)), this, SLOT(itemChanged(NeuroItem*)));
  89
  90        connect(network, SIGNAL(stepClicked()), this, SLOT(networkStepClicked()));
  91        connect(network, SIGNAL(postStep()), this, SLOT(networkPostStep()));
  92        connect(network, SIGNAL(stepFinished()), this, SLOT(networkStepFinished()));
  93
  94        connect(&_horizontal_property, SIGNAL(valueInBrowserChanged()), this, SLOT(propertyChanged()));
  95        connect(&_vertical_property, SIGNAL(valueInBrowserChanged()), this, SLOT(propertyChanged()));
  96    }
  97
  98    NeuroGridItem::~NeuroGridItem()
  99    {
 100    }
 101
 102    void NeuroGridItem::makeSubNetwork()
 103    {
 104        SubNetworkItem::makeSubNetwork();
 105        resizeScene();
 106    }
 107
 108    void NeuroGridItem::itemChanged(NeuroItem *item)
 109    {
 110        if (item && item->scene() && this->treeNode() && (item == this || item->scene() == this->treeNode()->scene()))
 111            _pattern_changed = true;
 112    }
 113
 114    void NeuroGridItem::propertyChanged()
 115    {
 116        _pattern_changed = true;
 117    }
 118
 119    void NeuroGridItem::networkStepClicked()
 120    {
 121        generateGrid();
 122    }
 123
 124    void NeuroGridItem::networkPostStep()
 125    {
 126    }
 127
 128    void NeuroGridItem::networkStepFinished()
 129    {
 130        // this gets set by networkChanged(), but since we've just finished a step,
 131        // we don't want to re-generate the grid unless something else changes
 132        _pattern_changed = false;
 133        _connections_changed = false;
 134
 135#ifdef DEBUG
 136        // dump graph
 137        {
 138            QFile file("neuronet_after_step.gv");
 139            if (file.open(QIODevice::WriteOnly))
 140            {
 141                QTextStream ts(&file);
 142                network()->neuronet()->dumpGraph(ts, true);
 143            }
 144        }
 145#endif
 146    }
 147
 148    void NeuroGridItem::copyColors()
 149    {
 150        // copy colors
 151        foreach (Index cell_index, _all_grid_cells)
 152        {
 153            if (_gl_line_colors.contains(cell_index))
 154            {
 155                int color_index = _gl_line_colors[cell_index];
 156                NeuroLib::NeuroNet::ASYNC_STATE *cell = getCell(cell_index);
 157                if (cell && color_index < _gl_line_color_array.size())
 158                {
 159                    qreal t = qBound(0.0f, cell->current().outputValue(), 1.0f);
 160                    QColor color = lerp(NORMAL_LINE_COLOR, ACTIVE_COLOR, t);
 161
 162                    // two vertices for lines
 163                    _gl_line_color_array[color_index++] = color.redF();
 164                    _gl_line_color_array[color_index++] = color.greenF();
 165                    _gl_line_color_array[color_index++] = color.blueF();
 166                    _gl_line_color_array[color_index++] = color.redF();
 167                    _gl_line_color_array[color_index++] = color.greenF();
 168                    _gl_line_color_array[color_index++] = color.blueF();
 169                }
 170            }
 171            else if (_gl_point_colors.contains(cell_index))
 172            {
 173                int color_index = _gl_point_colors[cell_index];
 174                NeuroLib::NeuroNet::ASYNC_STATE *cell = getCell(cell_index);
 175                if (cell && color_index < _gl_point_color_array.size())
 176                {
 177                    qreal t = qBound(0.0f, cell->current().outputValue(), 1.0f);
 178                    QColor color = lerp(NORMAL_LINE_COLOR, ACTIVE_COLOR, t);
 179
 180                    // two vertices for lines
 181                    _gl_point_color_array[color_index++] = color.redF();
 182                    _gl_point_color_array[color_index++] = color.greenF();
 183                    _gl_point_color_array[color_index++] = color.blueF();
 184                }
 185            }
 186        }
 187    }
 188
 189    void NeuroGridItem::resizeScene()
 190    {
 191        LabView *view = network()->view();
 192        Q_ASSERT(view);
 193
 194        QRect viewRect = view->viewport()->rect();
 195        QPointF topLeft = view->mapToScene(viewRect.topLeft());
 196        QPointF bottomRight = view->mapToScene(viewRect.bottomRight());
 197
 198        QRect newSceneRect(topLeft.toPoint(), bottomRight.toPoint());
 199
 200        treeNode()->scene()->setSceneRect(newSceneRect);
 201
 202        emit gridChanged();
 203    }
 204
 205    QList<NeuroGridItem::Index> NeuroGridItem::getIncomingCellsFor(const NeuroItem *item) const
 206    {
 207        NeuroNetworkItem *ni = const_cast<NeuroNetworkItem *>(dynamic_cast<const NeuroNetworkItem *>(item));
 208
 209        if (_top_connections.contains(ni))
 210            return _top_incoming;
 211        else if (_bottom_connections.contains(ni))
 212            return _bot_incoming;
 213
 214        return QList<Index>();
 215    }
 216
 217    QList<NeuroGridItem::Index> NeuroGridItem::getOutgoingCellsFor(const NeuroItem *item) const
 218    {
 219        NeuroNetworkItem *ni = const_cast<NeuroNetworkItem *>(dynamic_cast<const NeuroNetworkItem *>(item));
 220
 221        if (_top_connections.contains(ni))
 222            return _top_outgoing;
 223        else if (_bottom_connections.contains(ni))
 224            return _bot_outgoing;
 225
 226        return QList<Index>();
 227    }
 228
 229    void NeuroGridItem::addEdges(NeuroItem *)
 230    {
 231        // we need to adjust ALL items, not just this one!
 232        removeAllEdges();
 233        addAllEdges(0);
 234    }
 235
 236    void NeuroGridItem::removeEdges(NeuroItem *item)
 237    {
 238        // we need to adjust ALL edges!
 239        removeAllEdges();
 240        addAllEdges(item);
 241    }
 242
 243    struct ItemLessThan
 244    {
 245        NeuroItem *_targetItem;
 246
 247        ItemLessThan(NeuroItem *targetItem)
 248            : _targetItem(targetItem)
 249        {
 250        }
 251
 252        bool operator() (const NeuroItem *a, const NeuroItem *b)
 253        {
 254            int ax = a->scenePos().x();
 255            const MixinArrow *la = dynamic_cast<const MixinArrow *>(a);
 256            if (la)
 257            {
 258                if (_targetItem && la->frontLinkTarget() == _targetItem)
 259                    ax = la->line().p2().x();
 260                else if (_targetItem && la->backLinkTarget() == _targetItem)
 261                    ax = la->line().p1().x();
 262            }
 263
 264            int bx = b->scenePos().x();
 265            const MixinArrow *lb = dynamic_cast<const MixinArrow *>(b);
 266            if (lb)
 267            {
 268                if (_targetItem && lb->frontLinkTarget() == _targetItem)
 269                    bx = lb->line().p2().x();
 270                else if (_targetItem && lb->backLinkTarget() == _targetItem)
 271                    bx = lb->line().p1().x();
 272            }
 273
 274            return ax < bx;
 275        }
 276    };
 277
 278    static void connect_cell_groups(NeuroLib::NeuroNet *neuronet, const QList<NeuroNetworkItem::Index> & outgoing, const QList<NeuroNetworkItem::Index> & incoming)
 279    {
 280        const int num_groups = qMin(outgoing.size(), incoming.size());
 281        if (num_groups < 1)
 282            return;
 283
 284        const int out_step = outgoing.size() / num_groups;
 285        const int in_step = incoming.size() / num_groups;
 286
 287        for (int i = 0; i < num_groups; ++i)
 288        {
 289            for (int j = 0; j < out_step; ++j)
 290            {
 291                for (int k = 0; k < in_step; ++k)
 292                {
 293                    int out_index = i * out_step + j;
 294                    int in_index = i * in_step + k;
 295
 296                    if (out_index < outgoing.size() && in_index < incoming.size())
 297                        neuronet->addEdge(incoming[in_index], outgoing[out_index]);
 298                }
 299            }
 300        }
 301    }
 302
 303    void NeuroGridItem::addAllEdges(NeuroItem *except)
 304    {
 305        Q_ASSERT(network());
 306        Q_ASSERT(network()->neuronet());
 307        NeuroLib::NeuroNet *neuronet = network()->neuronet();
 308
 309        if (connections().size() == 0)
 310            return;
 311
 312        // get connections and sort by X coordinate
 313        QList<NeuroNetworkItem *> top_connected, bottom_connected;
 314
 315        foreach (NeuroItem *item, connections())
 316        {
 317            if (item != except)
 318            {
 319                NeuroNetworkItem *ni = dynamic_cast<NeuroNetworkItem *>(item);
 320
 321                if (_top_connections.contains(ni))
 322                    top_connected.append(ni);
 323                else if (_bottom_connections.contains(ni))
 324                    bottom_connected.append(ni);
 325            }
 326        }
 327
 328        qSort(top_connected.begin(), top_connected.end(), ItemLessThan(this));
 329        qSort(bottom_connected.begin(), bottom_connected.end(), ItemLessThan(this));
 330
 331        // get top cells
 332        QList<Index> top_incoming, top_outgoing;
 333        foreach (NeuroNetworkItem *ni, top_connected)
 334        {
 335            top_incoming.append(ni->getIncomingCellsFor(this));
 336            top_outgoing.append(ni->getOutgoingCellsFor(this));
 337        }
 338
 339        connect_cell_groups(neuronet, this->_top_outgoing, top_incoming);
 340        connect_cell_groups(neuronet, top_outgoing, this->_top_incoming);
 341
 342        // get bottom cells
 343        QList<Index> bottom_incoming, bottom_outgoing;
 344        foreach (NeuroNetworkItem *ni, bottom_connected)
 345        {
 346            bottom_incoming.append(ni->getIncomingCellsFor(this));
 347            bottom_outgoing.append(ni->getOutgoingCellsFor(this));
 348        }
 349
 350        connect_cell_groups(neuronet, this->_bot_outgoing, bottom_incoming);
 351        connect_cell_groups(neuronet, bottom_outgoing, this->_bot_incoming);
 352    }
 353
 354    void NeuroGridItem::removeAllEdges()
 355    {
 356        Q_ASSERT(network());
 357        Q_ASSERT(network()->neuronet());
 358        NeuroLib::NeuroNet *neuronet = network()->neuronet();
 359
 360        foreach (NeuroItem *item, this->connections())
 361        {
 362            NeuroNetworkItem *ni = dynamic_cast<NeuroNetworkItem *>(item);
 363            if (ni && _edges.contains(ni))
 364            {
 365                QMap<Index, Index> & item_edges = _edges[ni];
 366                QMap<Index, Index>::const_iterator i = item_edges.constBegin(), end = item_edges.constEnd();
 367                while (i != end)
 368                {
 369                    neuronet->removeEdge(i.key(), i.value());
 370                    ++i;
 371                }
 372                _edges.remove(ni);
 373            }
 374        }
 375    }
 376
 377    void NeuroGridItem::onEnterView()
 378    {
 379        resizeScene();
 380
 381        emit gridChanged();
 382    }
 383
 384    void NeuroGridItem::onLeaveView()
 385    {
 386        emit gridChanged();
 387    }
 388
 389    QPointF NeuroGridItem::targetPointFor(const NeuroItem *item, bool front) const
 390    {
 391        const MixinArrow *link = dynamic_cast<const MixinArrow *>(item);
 392        const NeuroNetworkItem *ni = dynamic_cast<const NeuroNetworkItem *>(item);
 393        if (link && ni)
 394        {
 395            QPointF pos = front ? link->line().p2() : link->line().p1();
 396
 397            if (_top_connections.contains(const_cast<NeuroNetworkItem *>(ni)))
 398                return QPointF(pos.x(), pos.y() + 20);
 399            else if (_bottom_connections.contains(const_cast<NeuroNetworkItem *>(ni)))
 400                return QPointF(pos.x(), pos.y() - 20);
 401        }
 402
 403        return scenePos();
 404    }
 405
 406    void NeuroGridItem::addToShape(QPainterPath & drawPath, QList<TextPathRec> & texts) const
 407    {
 408        NeuroNetworkItem::addToShape(drawPath, texts);
 409
 410        const QRectF & r = rect();
 411        drawPath.addRect(r);
 412
 413        const int num_vertical = 5;
 414        for (int i = 0; i < num_vertical; ++i)
 415        {
 416            int x = r.left() + (i+1)*r.width()/(num_vertical+1);
 417            drawPath.moveTo(x, r.top());
 418            drawPath.lineTo(x, r.top() + r.height());
 419        }
 420
 421        const int num_horizontal = 3;
 422        for (int i = 0; i < num_horizontal; ++i)
 423        {
 424            int y = r.top() + (i+1)*r.height()/(num_horizontal+1);
 425            drawPath.moveTo(r.left(), y);
 426            drawPath.lineTo(r.left() + r.width(), y);
 427        }
 428    }
 429
 430    bool NeuroGridItem::canCreateNewItem(const QString & typeName, const QPointF &) const
 431    {
 432        if (typeName.contains("ExcitoryLinkItem"))
 433            return true;
 434        if (typeName.contains("InhibitoryLinkItem"))
 435            return true;
 436        if (typeName.contains("NeuroNodeItem"))
 437            return true;
 438        if (typeName.contains("NeuroOscillatorItem"))
 439            return true;
 440        if (typeName.contains("TextItem"))
 441            return true;
 442        if (typeName.contains("GridEdgeItem"))
 443            return true;
 444
 445        return false;
 446    }
 447
 448    bool NeuroGridItem::canBeAttachedBy(const QPointF & pos, NeuroItem *item) const
 449    {
 450        MixinArrow *link = dynamic_cast<MixinArrow *>(item);
 451        MultiItem *mi = dynamic_cast<MultiItem *>(item);
 452
 453        if (!link && !mi) return false;
 454
 455        // only allow connections to the top or bottom
 456        QPointF topLeft = mapToScene(rect().topLeft());
 457        QPointF bottomRight = mapToScene(rect().bottomRight());
 458
 459        if (pos.y() > topLeft.y() && pos.y() < bottomRight.y())
 460            return false;
 461
 462        if (pos.y() <= topLeft.y())
 463        {
 464            foreach (NeuroNetworkItem *ni, _top_connections)
 465            {
 466                mi = dynamic_cast<MultiItem *>(ni);
 467                if (mi)
 468                    return false;
 469            }
 470        }
 471        else if (pos.y() >= bottomRight.y())
 472        {
 473            foreach (NeuroNetworkItem *ni, _bottom_connections)
 474            {
 475                mi = dynamic_cast<MultiItem *>(ni);
 476                if (mi)
 477                    return false;
 478            }
 479        }
 480
 481        return true;
 482    }
 483
 484    void NeuroGridItem::onSelected()
 485    {
 486        generateGrid();
 487        emit gridChanged();
 488    }
 489
 490    void NeuroGridItem::onAttachedBy(NeuroItem *item)
 491    {
 492        NeuroNetworkItem::onAttachedBy(item); // we don't want any sub-connection items
 493
 494        _connections_changed = true;
 495
 496        NeuroNetworkItem *ni = dynamic_cast<NeuroNetworkItem *>(item);
 497        if (ni)
 498        {
 499            QPointF mousePos = network()->scene()->lastMousePos();
 500
 501            bool top = mousePos.y() < scenePos().y();
 502            if (top)
 503                _top_connections.insert(ni);
 504            else
 505                _bottom_connections.insert(ni);
 506
 507            MultiGridIOItem *gi = dynamic_cast<MultiGridIOItem *>(ni);
 508            if (gi)
 509                adjustIOItem(gi, top);
 510        }
 511
 512        MixinArrow *link = dynamic_cast<MixinArrow *>(item);
 513        if (link)
 514        {
 515            MixinRemember::onAttachedBy(link);
 516        }
 517
 518        emit gridChanged();
 519    }
 520
 521    void NeuroGridItem::onDetach(NeuroItem *item)
 522    {
 523        _connections_changed = true;
 524
 525        NeuroNetworkItem *ni = dynamic_cast<NeuroNetworkItem *>(item);
 526        if (ni)
 527        {
 528            _top_connections.remove(ni);
 529            _bottom_connections.remove(ni);
 530        }
 531
 532        MixinArrow *link = dynamic_cast<MixinArrow *>(item);
 533        if (link)
 534        {
 535            MixinRemember::onDetach(link);
 536        }
 537
 538        NeuroNetworkItem::onDetach(item); // no subconnection items
 539
 540        emit gridChanged();
 541    }
 542
 543    void NeuroGridItem::adjustLinks()
 544    {
 545        SubNetworkItem::adjustLinks();
 546
 547        foreach (NeuroNetworkItem *item, _top_connections)
 548        {
 549            MultiGridIOItem *gi = dynamic_cast<MultiGridIOItem *>(item);
 550            if (gi)
 551                adjustIOItem(gi, true);
 552        }
 553
 554        foreach (NeuroNetworkItem *item, _bottom_connections)
 555        {
 556            MultiGridIOItem *gi = dynamic_cast<MultiGridIOItem *>(item);
 557            if (gi)
 558                adjustIOItem(gi, false);
 559        }
 560    }
 561
 562    void NeuroGridItem::adjustIOItem(MultiGridIOItem *gi, bool top)
 563    {
 564        if (top)
 565            gi->setPos(scenePos().x(), scenePos().y() - rect().height()/2 - gi->rect().height()/2);
 566        else
 567            gi->setPos(scenePos().x(), scenePos().y() + rect().height()/2 + gi->rect().height()/2);
 568    }
 569
 570    static QMap<NeuroGridItem::Index, NeuroGridItem::Index> &
 571    get_to_copy(QVector<QMap<NeuroGridItem::Index, NeuroGridItem::Index> > & all_copies, int row, int col, int num_cols)
 572    {
 573        return all_copies[(row * num_cols) + col];
 574    }
 575
 576    static double GL_WIDTH = 2;
 577    static double GL_HEIGHT = 3;
 578
 579    static void set_gl_vertex(int & vertex_index, QVector<float> & vertices, QVector<float> & colors,
 580                              QMap<NeuroGridItem::Index, int> & color_index,
 581                              NeuroGridItem::Index cell_index, const QPointF & pt,
 582                              float min_x, float max_x, float min_y, float max_y,
 583                              int col, int row, int num_cols, int num_rows)
 584    {
 585        float x, y, z;
 586
 587        float pat_x = (pt.x() - min_x) / (max_x - min_x);
 588        float pat_y = (pt.y() - min_y) / (max_y - min_y);
 589
 590        if (num_cols > 1)
 591        {
 592            float cyl_phi = (((float)col + pat_x)/(float)num_cols) * M_PI * 2;
 593
 594            x = ::cos(cyl_phi) * GL_WIDTH;
 595            z = -::sin(cyl_phi) * GL_WIDTH;
 596        }
 597        else
 598        {
 599            x = pat_x * GL_WIDTH - GL_WIDTH/2;
 600            z = 0;
 601        }
 602
 603        y = GL_HEIGHT/2 - (((float)row + pat_y)/(float)num_rows) * GL_HEIGHT;
 604
 605        if (cell_index != -1)
 606            color_index[cell_index] = vertex_index;
 607
 608        vertices[vertex_index + 0] = x;
 609        vertices[vertex_index + 1] = y;
 610        vertices[vertex_index + 2] = z;
 611
 612        colors[vertex_index + 0] = 0;
 613        colors[vertex_index + 1] = 0;
 614        colors[vertex_index + 2] = 0;
 615
 616        vertex_index += 3;
 617    }
 618
 619    static const int TOP = 0;
 620    static const int BOTTOM = 1;
 621    static const int LEFT = 2;
 622    static const int RIGHT = 3;
 623
 624    void NeuroGridItem::generateGrid()
 625    {
 626        if (!network() || network()->loading() || !network()->neuronet() || !scene() || !treeNode() || !treeNode()->scene())
 627            return;
 628
 629        NeuroLib::NeuroNet *neuronet = network()->neuronet();
 630
 631        if (_pattern_changed)
 632        {
 633            MainWindow::instance()->setStatus(tr("Generating neural network grid..."));
 634            QApplication::setOverrideCursor(Qt::WaitCursor);
 635
 636            removeAllEdges();
 637
 638            // remove old pattern cells
 639            foreach (const Index & index, _all_grid_cells)
 640            {
 641                neuronet->removeNode(index);
 642            }
 643            _all_grid_cells.clear();
 644
 645            // get pattern cells; map pattern cells to 2d pattern positions
 646            QVector<Index> all_pattern_cells;
 647            QVector<QMap<Index, QVector<Index> > > pattern_connections(4);
 648            QVector<Index> pattern_top_incoming, pattern_top_outgoing;
 649            QVector<Index> pattern_bot_incoming, pattern_bot_outgoing;
 650            QMap<Index, QLineF> pattern_cells_to_lines;
 651            QMap<Index, QPointF> pattern_cells_to_points;
 652            bool hasTopEdge, hasBottomEdge, hasLeftEdge, hasRightEdge;
 653
 654            collectPatternCells(neuronet,
 655                                all_pattern_cells, pattern_connections,
 656                                pattern_top_incoming, pattern_top_outgoing,
 657                                pattern_bot_incoming, pattern_bot_outgoing,
 658                                pattern_cells_to_lines, pattern_cells_to_points,
 659                                hasTopEdge, hasBottomEdge, hasLeftEdge, hasRightEdge);
 660
 661            //  get min and max coordinate extents
 662            float min_x, max_x, min_y, max_y;
 663            getMinMaxCoords(pattern_cells_to_lines, pattern_cells_to_points,
 664                            min_x, max_x, min_y, max_y,
 665                            hasTopEdge, hasBottomEdge, hasLeftEdge, hasRightEdge);
 666
 667            // make enough copies of the pattern, and connect their internal edges
 668            QVector<QMap<Index, Index> > all_copies(_num_vert * _num_horiz);
 669            makeCopies(neuronet, all_pattern_cells, pattern_cells_to_lines, pattern_cells_to_points,
 670                       all_copies, min_x, max_x, min_y, max_y);
 671
 672            // connect outside edges
 673            connectCopies(neuronet, all_copies, pattern_connections,
 674                          pattern_top_incoming, pattern_top_outgoing,
 675                          pattern_bot_incoming, pattern_bot_outgoing);
 676
 677            // done
 678            addAllEdges(0);
 679            _pattern_changed = false;
 680            _connections_changed = false;
 681
 682            copyColors();
 683            QApplication::restoreOverrideCursor();
 684            MainWindow::instance()->setStatus(tr("Generated neural network grid."));
 685
 686            emit gridChanged();
 687
 688    #ifdef DEBUG
 689            // dump graph
 690            {
 691                QFile file("neuronet_after_gen.gv");
 692                if (file.open(QIODevice::WriteOnly))
 693                {
 694                    QTextStream ts(&file);
 695                    neuronet->dumpGraph(ts, true);
 696                }
 697            }
 698    #endif
 699        }
 700        else if (_connections_changed)
 701        {
 702            removeAllEdges();
 703            addAllEdges(0);
 704            _connections_changed = false;
 705        }
 706    }
 707
 708    void NeuroGridItem::connectCopies(NeuroLib::NeuroNet *neuronet,
 709                                      QVector<QMap<Index, Index> > & all_copies,
 710                                      QVector<QMap<Index, QVector<Index> > > & pattern_connections,
 711                                      QVector<Index> & pattern_top_incoming, QVector<Index> & pattern_top_outgoing,
 712                                      QVector<Index> & pattern_bot_incoming, QVector<Index> & pattern_bot_outgoing)
 713    {
 714        _top_incoming.clear();
 715        _top_outgoing.clear();
 716        _bot_incoming.clear();
 717        _bot_outgoing.clear();
 718
 719        for (int row = 0; row < _num_vert; ++row)
 720        {
 721            for (int col = 0; col < _num_horiz; ++col)
 722            {
 723                int left_col = (col + _num_horiz - 1) % _num_horiz;
 724                int right_col = (col + 1) % _num_horiz;
 725                int up_row = row - 1;
 726                int down_row = row + 1;
 727
 728                const QMap<Index, Index> & pat_to_copy = get_to_copy(all_copies, row, col, _num_horiz);
 729
 730                // top
 731                if (row == 0)
 732                {
 733                    foreach (const Index index, pattern_top_incoming)
 734                        _top_incoming.append(pat_to_copy[index]);
 735                    foreach (const Index index, pattern_top_outgoing)
 736                        _top_outgoing.append(pat_to_copy[index]);
 737                }
 738                else
 739                {
 740                    const QMap<Index, Index> & pat_to_copy_above = get_to_copy(all_copies, up_row, col, _num_horiz);
 741
 742                    foreach (const Index pat_in, pattern_connections[TOP].keys())
 743                    {
 744                        const Index copy_in = pat_to_copy[pat_in];
 745
 746                        foreach (const Index pat_out, pattern_connections[TOP][pat_in])
 747                        {
 748                            const Index copy_out = pat_to_copy_above[pat_out];
 749                            neuronet->addEdge(copy_in, copy_out);
 750                        }
 751                    }
 752                }
 753
 754                // bottom
 755                if (row == _num_vert - 1)
 756                {
 757                    foreach (const Index index, pattern_bot_incoming)
 758                        _bot_incoming.append(pat_to_copy[index]);
 759                    foreach (const Index index, pattern_bot_outgoing)
 760                        _bot_outgoing.append(pat_to_copy[index]);
 761                }
 762                else
 763                {
 764                    const QMap<Index, Index> & pat_to_copy_below = get_to_copy(all_copies, down_row, col, _num_horiz);
 765
 766                    foreach (const Index pat_in, pattern_connections[BOTTOM].keys())
 767                    {
 768                        const Index copy_in = pat_to_copy[pat_in];
 769
 770                        foreach (const Index pat_out, pattern_connections[BOTTOM][pat_in])
 771                        {
 772                            const Index copy_out = pat_to_copy_below[pat_out];
 773                            neuronet->addEdge(copy_in, copy_out);
 774                        }
 775                    }
 776                }
 777
 778                // left
 779                const QMap<Index, Index> & pat_to_copy_left = get_to_copy(all_copies, row, left_col, _num_horiz);
 780
 781                foreach (const Index pat_in, pattern_connections[LEFT].keys())
 782                {
 783                    const Index copy_in = pat_to_copy[pat_in];
 784
 785                    foreach (const Index pat_out, pattern_connections[LEFT][pat_in])
 786                    {
 787                        const Index copy_out = pat_to_copy_left[pat_out];
 788                        neuronet->addEdge(copy_in, copy_out);
 789                    }
 790                }
 791
 792                // right
 793                const QMap<Index, Index> & pat_to_copy_right = get_to_copy(all_copies, row, right_col, _num_horiz);
 794
 795                foreach (const Index pat_in, pattern_connections[RIGHT].keys())
 796                {
 797                    const Index copy_in = pat_to_copy[pat_in];
 798
 799                    foreach (const Index pat_out, pattern_connections[RIGHT][pat_in])
 800                    {
 801                        const Index copy_out = pat_to_copy_right[pat_out];
 802                        neuronet->addEdge(copy_in, copy_out);
 803                    }
 804                }
 805            }
 806        }
 807    }
 808
 809    void NeuroGridItem::makeCopies(NeuroLib::NeuroNet *neuronet,
 810                                   QVector<Index> & all_pattern_cells,
 811                                   QMap<Index, QLineF> & pattern_cells_to_lines,
 812                                   QMap<Index, QPointF> & pattern_cells_to_points,
 813                                   QVector<QMap<Index, Index> > & all_copies,
 814                                   float min_x, float max_x, float min_y, float max_y)
 815    {
 816        _gl_line_array.resize(_num_vert * _num_horiz * pattern_cells_to_lines.size() * 6);
 817        _gl_line_color_array.resize(_num_vert * _num_horiz * pattern_cells_to_lines.size() * 6);
 818        _gl_point_array.resize(_num_vert * _num_horiz * pattern_cells_to_points.size() * 3);
 819        _gl_point_color_array.resize(_num_vert * _num_horiz * pattern_cells_to_points.size() * 3);
 820
 821        int line_index = 0;
 822        int point_index = 0;
 823
 824        // copy cells
 825        for (int row = 0; row < _num_vert; ++row)
 826        {
 827            for (int col = 0; col < _num_horiz; ++col)
 828            {
 829                QMap<Index, Index> & pat_to_copy = get_to_copy(all_copies, row, col, _num_horiz);
 830
 831                // copy pattern cells
 832                foreach (const Index pat_index, all_pattern_cells)
 833                {
 834                    Index copy_index = neuronet->addNode((*neuronet)[pat_index].current());
 835                    _all_grid_cells.insert(copy_index);
 836                    pat_to_copy[pat_index] = copy_index;
 837
 838                    // add geometry info for the viewer
 839                    if (pattern_cells_to_lines.contains(pat_index))
 840                    {
 841                        const QLineF ln = pattern_cells_to_lines[pat_index];
 842                        set_gl_vertex(line_index, _gl_line_array, _gl_line_color_array,
 843                                      _gl_line_colors, copy_index, ln.p1(),
 844                                      min_x, max_x, min_y, max_y, col, row, _num_horiz, _num_vert);
 845                        set_gl_vertex(line_index, _gl_line_array, _gl_line_color_array,
 846                                      _gl_line_colors, -1, ln.p2(),
 847                                      min_x, max_x, min_y, max_y, col, row, _num_horiz, _num_vert);
 848                    }
 849                    else if (pattern_cells_to_points.contains(pat_index))
 850                    {
 851                        const QPointF pt = pattern_cells_to_points[pat_index];
 852                        set_gl_vertex(point_index, _gl_point_array, _gl_point_color_array,
 853                                      _gl_point_colors, copy_index, pt,
 854                                      min_x, max_x, min_y, max_y, col, row, _num_horiz, _num_vert);
 855                    }
 856                }
 857            }
 858        }
 859
 860        // now copy internal connections
 861        for (int row = 0; row < _num_vert; ++row)
 862        {
 863            for (int col = 0; col < _num_horiz; ++col)
 864            {
 865                QMap<Index, Index> & pat_to_copy = get_to_copy(all_copies, row, col, _num_horiz);
 866
 867                foreach (const Index pat_index, all_pattern_cells)
 868                {
 869                    const Index copy_index = pat_to_copy[pat_index];
 870                    foreach (const Index pat_neighbor, neuronet->neighbors(pat_index))
 871                    {
 872                        const Index copy_neighbor = pat_to_copy[pat_neighbor];
 873                        neuronet->addEdge(copy_index, copy_neighbor);
 874                    }
 875                }
 876            }
 877        }
 878    }
 879
 880    void NeuroGridItem::getMinMaxCoords(QMap<Index, QLineF> & pattern_cells_to_lines,
 881                                        QMap<Index, QPointF> & pattern_cells_to_points,
 882                                        float & min_x, float & max_x, float & min_y, float & max_y,
 883                                        bool & hasTopEdge, bool & hasBottomEdge, bool & hasLeftEdge, bool & hasRightEdge)
 884    {
 885        min_x = FLT_MAX;
 886        max_x = -min_x;
 887        min_y = FLT_MAX;
 888        max_y = -min_y;
 889
 890        foreach (QLineF line, pattern_cells_to_lines)
 891        {
 892            QPointF back = line.p1();
 893            min_x = qMin(min_x, (float)back.x());
 894            max_x = qMax(max_x, (float)back.x());
 895            min_y = qMin(min_y, (float)back.y());
 896            max_y = qMax(max_y, (float)back.y());
 897
 898            QPointF front = line.p2();
 899            min_x = qMin(min_x, (float)front.x());
 900            max_x = qMax(max_x, (float)front.x());
 901            min_y = qMin(min_y, (float)front.y());
 902            max_y = qMax(max_y, (float)front.y());
 903        }
 904
 905        foreach (QPointF pt, pattern_cells_to_points)
 906        {
 907            min_x = qMin(min_x, (float)pt.x());
 908            max_x = qMax(max_x, (float)pt.x());
 909            min_y = qMin(min_y, (float)pt.y());
 910            max_y = qMax(max_y, (float)pt.y());
 911        }
 912
 913        if ((max_x - min_x) < 1)
 914        {
 915            max_x = min_x + 1;
 916            min_x = max_x - 2;
 917        }
 918        if ((max_y - min_y) < 1)
 919        {
 920            max_y = min_y + 1;
 921            min_y = max_y - 2;
 922        }
 923
 924        if (!hasLeftEdge)
 925            min_x -= 20;
 926        if (!hasRightEdge)
 927            max_x += 20;
 928        if (!hasTopEdge)
 929            min_y -= 20;
 930        if (!hasBottomEdge)
 931            max_y += 20;
 932    }
 933
 934    void NeuroGridItem::collectPatternCells(NeuroLib::NeuroNet *,
 935                                            QVector<Index> &all_pattern_cells,
 936                                            QVector<QMap<Index, QVector<Index> > > & pattern_connections,
 937                                            QVector<Index> &pattern_top_incoming, QVector<Index> &pattern_top_outgoing,
 938                                            QVector<Index> &pattern_bot_incoming, QVector<Index> &pattern_bot_outgoing,
 939                                            QMap<Index, QLineF> &pattern_cells_to_lines,
 940                                            QMap<Index, QPointF> &pattern_cells_to_points,
 941                                            bool &hasTopEdge, bool &hasBottomEdge,
 942                                            bool &hasLeftEdge, bool &hasRightEdge)
 943    {
 944        hasTopEdge = hasBottomEdge = hasLeftEdge = hasRightEdge = false;
 945
 946        // get neuro items and sort by X coordinate
 947        QVector<NeuroItem *> pattern_items;
 948        foreach (QGraphicsItem *item, treeNode()->scene()->items())
 949        {
 950            NeuroItem *ni = dynamic_cast<NeuroItem *>(item);
 951            if (ni)
 952                pattern_items.append(ni);
 953        }
 954
 955        qSort(pattern_items.begin(), pattern_items.end(), ItemLessThan(0));
 956
 957        // go through items and record coordinates
 958        // for edge items, record connections and edge cells
 959        foreach (NeuroItem *item, pattern_items)
 960        {
 961            NeuroNetworkItem *ni = dynamic_cast<NeuroNetworkItem *>(item);
 962            if (!ni)
 963                continue;
 964
 965            // record pattern positions
 966            recordPatternPositions(ni, pattern_cells_to_lines, pattern_cells_to_points);
 967
 968            // all normal connections
 969            foreach (Index index, ni->allCells())
 970            {
 971                all_pattern_cells.append(index);
 972            }
 973
 974            // edge connections
 975            GridEdgeItem *ei = dynamic_cast<GridEdgeItem *>(item);
 976            if (!ei)
 977                continue;
 978
 979            QVector<Index> top_incoming, top_outgoing;
 980            QVector<Index> bot_incoming, bot_outgoing;
 981            QVector<Index> left_incoming, left_outgoing;
 982            QVector<Index> right_incoming, right_outgoing;
 983
 984            foreach (NeuroItem *cx, ei->connections())
 985            {
 986                MixinArrow *link = dynamic_cast<MixinArrow *>(cx);
 987                if (!link)
 988                    continue;
 989
 990                QList<Index> frontward = link->getFrontwardCells();
 991                QList<Index> backward = link->getBackwardCells();
 992
 993                bool frontConnected;
 994
 995                if (ei->isConnectedToTop(link, frontConnected))
 996                {
 997                    hasTopEdge = true;
 998
 999                    if (frontConnected)
1000                    {
1001                        if (frontward.size() > 0)
1002                            top_outgoing.append(frontward.last());
1003                        if (backward.size() > 0)
1004                            top_incoming.append(backward.first());
1005                    }
1006                    else
1007                    {
1008                        if (frontward.size() > 0)
1009                            top_incoming.append(frontward.first());
1010                        if (backward.size() > 0)
1011                            top_outgoing.append(backward.last());
1012                    }
1013                }
1014                if (ei->isConnectedToBottom(link, frontConnected))
1015                {
1016                    hasBottomEdge = true;
1017
1018                    if (frontConnected)
1019                    {
1020                        if (frontward.size() > 0)
1021                            bot_outgoing.append(frontward.last());
1022                        if (backward.size() > 0)
1023                            bot_incoming.append(backward.first());
1024                    }
1025                    else
1026                    {
1027                        if (frontward.size() > 0)
1028                            bot_incoming.append(frontward.first());
1029                        if (backward.size() > 0)
1030                            bot_outgoing.append(backward.last());
1031                    }
1032                }
1033                if (ei->isConnectedToLeft(link, frontConnected))
1034                {
1035                    hasLeftEdge = true;
1036
1037                    if (frontConnected)
1038                    {
1039                        if (frontward.size() > 0)
1040                            left_outgoing.append(frontward.last());
1041                        if (backward.size() > 0)
1042                            left_incoming.append(backward.first());
1043                    }
1044                    else
1045                    {
1046                        if (frontward.size() > 0)
1047                            left_incoming.append(frontward.first());
1048                        if (backward.size() > 0)
1049                            left_outgoing.append(backward.last());
1050                    }
1051                }
1052                if (ei->isConnectedToRight(link, frontConnected))
1053                {
1054                    hasRightEdge = true;
1055
1056                    if (frontConnected)
1057                    {
1058                        if (frontward.size() > 0)
1059                            right_outgoing.append(frontward.last());
1060                        if (backward.size() > 0)
1061                            right_incoming.append(backward.first());
1062                    }
1063                    else
1064                    {
1065                        if (frontward.size() > 0)
1066                            right_incoming.append(frontward.first());
1067                        if (backward.size() > 0)
1068                            right_outgoing.append(backward.last());
1069                    }
1070                }
1071            } // for each connection, collect connecting cells
1072
1073            // now connect up all connections for this edge item
1074            foreach (const Index in, top_incoming)
1075                pattern_connections[TOP][in] += bot_outgoing;
1076
1077            foreach (const Index in, bot_incoming)
1078                pattern_connections[BOTTOM][in] += top_outgoing;
1079
1080            foreach (const Index in, left_incoming)
1081                pattern_connections[LEFT][in] += right_outgoing;
1082
1083            foreach (const Index in, right_incoming)
1084                pattern_connections[RIGHT][in] += left_outgoing;
1085
1086            // add to pattern lists
1087            pattern_top_incoming += top_incoming;
1088            pattern_top_outgoing += top_outgoing;
1089            pattern_bot_incoming += bot_incoming;
1090            pattern_bot_outgoing += bot_outgoing;
1091        } // for each pattern item
1092    } // collectPatternCells()
1093
1094    void NeuroGridItem::recordPatternPositions(NeuroNetworkItem *ni,
1095                                QMap<Index, QLineF> & pattern_cells_to_lines,
1096                                QMap<Index, QPointF> & pattern_cells_to_points)
1097    {
1098        MixinArrow *link = dynamic_cast<MixinArrow *>(ni);
1099        if (link)
1100        {
1101            QLineF line = link->line();
1102            QVector2D back(line.p1());
1103            QVector2D front(line.p2());
1104            QVector2D back_to_front = front - back;
1105            QVector2D front_to_back = back - front;
1106
1107            QList<Index> frontwardCells = link->getFrontwardCells();
1108            int i = 0;
1109            double num_cells = frontwardCells.size();
1110            foreach (Index index, frontwardCells)
1111            {
1112                Q_ASSERT(index != -1);
1113
1114                QVector2D start = back + back_to_front * (static_cast<double>(i) / num_cells);
1115                QVector2D end = back + back_to_front * (static_cast<double>(i+1) / num_cells);
1116
1117                pattern_cells_to_lines[index] = QLineF(start.toPointF(), end.toPointF());
1118                ++i;
1119            }
1120
1121            QList<Index> backwardCells = link->getBackwardCells();
1122            i = 0; num_cells = backwardCells.size();
1123            foreach (Index index, backwardCells)
1124            {
1125                Q_ASSERT(index != -1);
1126
1127                QVector2D start = front + front_to_back * (static_cast<double>(i) / num_cells);
1128                QVector2D end = front + front_to_back * (static_cast<double>(i+1) / num_cells);
1129
1130                pattern_cells_to_lines[index] = QLineF(start.toPointF(), end.toPointF());
1131                ++i;
1132            }
1133        }
1134        else
1135        {
1136            foreach (Index index, ni->allCells())
1137            {
1138                Q_ASSERT(index != -1);
1139                pattern_cells_to_points[index] = ni->scenePos();
1140            }
1141        }
1142    }
1143
1144    template <typename TCollection, typename TData, typename TWrite>
1145    static void write_collection(QDataStream & ds, const TCollection & collection)
1146    {
1147        ds << static_cast<quint32>(collection.size());
1148        foreach (const TData & data, collection)
1149        {
1150            ds << static_cast<TWrite>(data);
1151        }
1152    }
1153
1154    void NeuroGridItem::writeBinary(QDataStream &ds, const NeuroLabFileVersion &file_version) const
1155    {
1156        const_cast<NeuroGridItem *>(this)->generateGrid();
1157
1158        SubNetworkItem::writeBinary(ds, file_version);
1159
1160        ds << static_cast<quint32>(_num_horiz);
1161        ds << static_cast<quint32>(_num_vert);
1162
1163        // write edges
1164        ds << static_cast<quint32>(_edges.size());
1165        QMap<NeuroNetworkItem *, QMap<Index, Index> >::const_iterator i = _edges.constBegin(), i_end = _edges.constEnd();
1166        while (i != i_end)
1167        {
1168            if (i.key())
1169            {
1170                ds << static_cast<IdType>(i.key()->id());
1171
1172                ds << static_cast<quint32>(i.value().size());
1173                QMap<Index, Index>::const_iterator j = i.value().constBegin(), j_end = i.value().constEnd();
1174                while (j != j_end)
1175                {
1176                    ds << j.key();
1177                    ds << j.value();
1178                    ++j;
1179                }
1180            }
1181            ++i;
1182        }
1183
1184        // write cells and graphics
1185        write_collection<QSet<Index>, Index, quint32>(ds, _all_grid_cells);
1186        write_collection<QList<Index>, Index, quint32>(ds, _top_incoming);
1187        write_collection<QList<Index>, Index, quint32>(ds, _top_outgoing);
1188        write_collection<QList<Index>, Index, quint32>(ds, _bot_incoming);
1189        write_collection<QList<Index>, Index, quint32>(ds, _bot_outgoing);
1190        write_collection<QVector<float>, float, float>(ds, _gl_line_array);
1191        write_collection<QVector<float>, float, float>(ds, _gl_point_array);
1192        write_collection<QVector<float>, float, float>(ds, _gl_line_color_array);
1193        write_collection<QVector<float>, float, float>(ds, _gl_point_color_array);
1194
1195        ds << static_cast<quint32>(_gl_line_colors.size());
1196        foreach (const Index & index, _gl_line_colors.keys())
1197        {
1198            ds << static_cast<quint32>(index);
1199            ds << static_cast<quint32>(_gl_line_colors[index]);
1200        }
1201
1202        ds << static_cast<quint32>(_gl_point_colors.size());
1203        foreach (const Index & index, _gl_point_colors.keys())
1204        {
1205            ds << static_cast<quint32>(index);
1206            ds << static_cast<quint32>(_gl_point_colors[index]);
1207        }
1208    }
1209
1210    template <typename TCollection, typename TData, typename TRead>
1211    static void read_collection(QDataStream & ds, TCollection & collection)
1212    {
1213        collection.clear();
1214
1215        quint32 size;
1216        ds >> size;
1217
1218        for (quint32 i = 0; i < size; ++i)
1219        {
1220            TRead val;
1221            ds >> val;
1222
1223            collection.append(static_cast<TData>(val));
1224        }
1225    }
1226
1227    void NeuroGridItem::readBinary(QDataStream &ds, const NeuroLabFileVersion &file_version)
1228    {
1229        SubNetworkItem::readBinary(ds, file_version);
1230
1231        if (file_version.neurolab_version >= NeuroGui::NEUROLAB_FILE_VERSION_9)
1232        {
1233            quint32 num;
1234            ds >> num; _num_horiz = num;
1235            ds >> num; _num_vert = num;
1236
1237            // read edges
1238            _edges.clear();
1239            if (file_version.neurolab_version >= NeuroGui::NEUROLAB_FILE_VERSION_11)
1240            {
1241                quint32 num_i;
1242                ds >> num_i;
1243                for (quint32 i = 0; i < num_i; ++i)
1244                {
1245                    IdType id;
1246                    ds >> id;
1247                    NeuroNetworkItem *id_ptr = reinterpret_cast<NeuroNetworkItem *>(id);
1248                    QMap<Index, Index> & edge_map = _edges[id_ptr];
1249
1250                    quint32 num_j;
1251                    ds >> num_j;
1252                    for (quint32 j = 0; j < num_j; ++j)
1253                    {
1254                        Index key, val;
1255                        ds >> key;
1256                        ds >> val;
1257                        edge_map.insert(key, val);
1258                    }
1259                }
1260            }
1261
1262            // cells and graphics
1263            if (file_version.neurolab_version >= NeuroGui::NEUROLAB_FILE_VERSION_13)
1264            {
1265                _all_grid_cells.clear();
1266                ds >> num;
1267                for (quint32 i = 0; i < num; ++i)
1268                {
1269                    quint32 val;
1270                    ds >> val;
1271                    _all_grid_cells.insert(static_cast<Index>(val));
1272                }
1273
1274                read_collection<QList<Index>, Index, quint32>(ds, _top_incoming);
1275                read_collection<QList<Index>, Index, quint32>(ds, _top_outgoing);
1276                read_collection<QList<Index>, Index, quint32>(ds, _bot_incoming);
1277                read_collection<QList<Index>, Index, quint32>(ds, _bot_outgoing);
1278                read_collection<QVector<float>, float, float>(ds, _gl_line_array);
1279                read_collection<QVector<float>, float, float>(ds, _gl_point_array);
1280                read_collection<QVector<float>, float, float>(ds, _gl_line_color_array);
1281                read_collection<QVector<float>, float, float>(ds, _gl_point_color_array);
1282
1283                _gl_line_colors.clear();
1284                ds >> num;
1285                for (quint32 i = 0; i < num; ++i)
1286                {
1287                    quint32 index, val;
1288                    ds >> index;
1289                    ds >> val;
1290
1291                    _gl_line_colors[static_cast<Index>(index)] = static_cast<int>(val);
1292                }
1293
1294                _gl_point_colors.clear();
1295                ds >> num;
1296                for (quint32 i = 0; i < num; ++i)
1297                {
1298                    quint32 index, val;
1299                    ds >> index;
1300                    ds >> val;
1301
1302                    _gl_point_colors[static_cast<Index>(index)] = static_cast<int>(val);
1303                }
1304            }
1305        }
1306    }
1307
1308    void NeuroGridItem::writePointerIds(QDataStream &ds, const NeuroLabFileVersion &file_version) const
1309    {
1310        SubNetworkItem::writePointerIds(ds, file_version);
1311
1312        quint32 num = _top_connections.size();
1313        ds << num;
1314        foreach (const NeuroNetworkItem *ni, _top_connections)
1315            ds << static_cast<IdType>(ni ? ni->id() : 0);
1316
1317        num = _bottom_connections.size();
1318        ds << num;
1319        foreach (const NeuroNetworkItem *ni, _bottom_connections)
1320            ds << static_cast<IdType>(ni ? ni->id() : 0);
1321    }
1322
1323    void NeuroGridItem::readPointerIds(QDataStream &ds, const NeuroLabFileVersion &file_version)
1324    {
1325        SubNetworkItem::readPointerIds(ds, file_version);
1326
1327        if (file_version.neurolab_version >= NeuroGui::NEUROLAB_FILE_VERSION_9)
1328        {
1329            quint32 num;
1330
1331            _top_connections.clear();
1332            ds >> num;
1333            for (quint32 i = 0; i < num; ++i)
1334            {
1335                IdType id;
1336                ds >> id;
1337                if (id) _top_connections.insert(reinterpret_cast<NeuroNetworkItem *>(id));
1338            }
1339
1340            _bottom_connections.clear();
1341            ds >> num;
1342            for (quint32 i = 0; i < num; ++i)
1343            {
1344                IdType id;
1345                ds >> id;
1346                if (id) _bottom_connections.insert(reinterpret_cast<NeuroNetworkItem *>(id));
1347            }
1348        }
1349    }
1350
1351    void NeuroGridItem::idsToPointers(const QMap<NeuroItem::IdType, NeuroItem *> &idMap)
1352    {
1353        SubNetworkItem::idsToPointers(idMap);
1354
1355        QSet<NeuroNetworkItem *> toAdd;
1356
1357        foreach (NeuroNetworkItem *ni, _top_connections)
1358        {
1359            IdType wanted_id = static_cast<NeuroItem::IdType>(reinterpret_cast<quint64>(ni));
1360            NeuroNetworkItem *wanted_item = dynamic_cast<NeuroNetworkItem *>(idMap[wanted_id]);
1361
1362            if (wanted_item)
1363                toAdd.in

Large files files are truncated, but you can click here to view the full file