source: cpp/frams/canvas/neurodiagram.cpp @ 1100

Last change on this file since 1100 was 973, checked in by Maciej Komosinski, 4 years ago

Increased SString and std::string compatibility: introduced length(), size(), and capacity(), and removed legacy methods that have std::string equivalents

  • Property svn:eol-style set to native
File size: 17.7 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include "neurodiagram.h"
6#include "nn_layout.h"
7#include <frams/neuro/neurolibrary.h>
8#include <frams/mech/mechworld.h>
9#include <frams/util/multirange.h>
10#include "canvasutil.h"
11#include <frams/neuro/neuroimpl.h>
12#include <frams/model/modelobj.h>
13#include <frams/simul/simul.h>
14#include "common/nonstd_time.h"
15
16#define FIELDSTRUCT NeuroDiagram
17ParamEntry neurodiagram_paramtab[] =
18{
19        { "NeuroDiagram", 1, 4, "NeuroDiagram", "Can be used as the client object in the Window.", },
20        { "new", 0, PARAM_USERHIDDEN, "create new NeuroDiagram", "p oNeuroDiagram", PROCEDURE(p_new), },
21        { "showCreature", 0, PARAM_USERHIDDEN | PARAM_NOSTATIC, "show dynamic NN", "p(oCreature)", PROCEDURE(p_showcr), },
22        { "showModel", 0, PARAM_USERHIDDEN | PARAM_NOSTATIC, "show static NN", "p(oModel)", PROCEDURE(p_showmod), },
23        { "hide", 0, PARAM_USERHIDDEN | PARAM_NOSTATIC, "hide NN", "p()", PROCEDURE(p_hide), },
24        { 0, 0, 0, },
25};
26#undef FIELDSTRUCT
27
28Param neurodiagram_param(neurodiagram_paramtab, 0);
29
30static struct ColorDefs colordefs;
31
32void NeuroDiagram::p_new(ExtValue*args, ExtValue*ret)
33{
34        NeuroDiagram *d = new NeuroDiagram(&colordefs);
35        d->drawbackground = false;
36        ret->setObject(ExtObject(&neurodiagram_param, d));
37}
38
39void NeuroDiagram::p_showcr(ExtValue*args, ExtValue*ret)
40{
41        Creature *cr = 0;
42        if (args->type == TObj)
43        {
44                const ExtObject& o = args->getObject();
45                cr = (Creature*)o.getTarget();
46        }
47        showLive(cr);
48}
49
50void NeuroDiagram::p_showmod(ExtValue*args, ExtValue*ret)
51{
52        Model *mod = ModelObj::fromObject(args[0]);
53        show(mod);
54}
55
56static void addNeuroDescription(SString &t, Neuro *n)
57{
58        static Param par;
59        SString c(n->getClassName());
60        NeuroClass* cl = n->getClass();
61        t += c;
62        t += " (";
63        if (cl)
64                t += cl->getLongName();
65        else
66                t += "Unknown";
67        t += ")";
68}
69
70NeuroDiagram::NeuroDiagram(ColorDefs *cd)
71        :FramDrawToolkit(cd), livewire(false), indestructor(false), showing_not_alive_label(false), o(0),
72        warn_if_not_alive(true), selection(*this), drawbackground(true), linetype(true), layouttype(2)
73{
74        scroll.setMargin(10, 10); // appropriate size should be adjusted
75        dontPaintOutside(0);
76        add(&scroll);
77        pluginactive = false;
78        FramDrawToolkit::setBackColor(ColorDefs::neurobackground);
79}
80
81NeuroDiagram::~NeuroDiagram()
82{
83        indestructor = 1;
84        hide();
85        remove(&scroll);
86        updatePlugin();
87}
88
89void NeuroDiagram::hide()
90{
91        showing_not_alive_label = 0;
92        if (o) o->delmodel_list.remove(killnode);
93        FOREACH(NeuroProbe*, pr, probes)
94                delete pr;
95        probes.clear();
96        selection.clear(0);
97        cr = 0;
98        livewire = false;
99}
100
101class NNLayoutState_Neurodiagram : public NNLayoutState
102{
103public:
104        NeuroDiagram *nd;
105        NNLayoutState_Neurodiagram(NeuroDiagram *_nd) :nd(_nd) {}
106
107        int GetElements()
108        {
109                return nd->scroll.count();
110        }
111
112        int *GetXYWH(int el)
113        {
114                return &nd->scroll.getInfo(el)->pos.x;
115        }
116
117        void SetXYWH(int el, int x, int y, int w, int h)
118        {
119                ScrollInfo *si = nd->scroll.getInfo(el);
120                si->pos.set(x, y); si->size.set(w, h);
121        }
122
123        int GetInputs(int el)
124        {
125                return nd->getNS(el)->n->getInputCount();
126        }
127
128        int GetLink(int el, int i)
129        {
130                return nd->getNS(el)->n->getInput(i)->refno;
131        }
132
133        int *GetLinkXY(int el, int i)
134        {
135                static int XY[2];
136                int *xywh = GetXYWH(el);
137                XY[0] = 0;
138                XY[1] = ((1 + i) * xywh[3]) / (GetInputs(el) + 1);
139                return XY;
140        }
141};
142
143
144void NeuroDiagram::show(Model *o_)
145{
146        hide();
147        o = o_;
148        scroll.removeAll();
149        if (o)
150        {
151                Neuro *n;
152                int i;
153                killnode = o->delmodel_list.add(STATRICKCALLBACK(this, &NeuroDiagram::onKill, 0));
154
155                // create symbol objects
156                for (i = 0; n = o->getNeuro(i); i++)
157                {
158                        NeuroSymbol *ns = new NeuroSymbol(*this, n);
159                        scroll.add(ns, 1); // autodel   
160                }
161                if (i)
162                {
163                        struct NNLayoutFunction *nnfun = &nn_layout_functions[layouttype];
164                        NNLayoutState_Neurodiagram nn(this);
165                        nnfun->doLayout(&nn);
166                }
167                scroll.invalidate();
168                scroll.autoZoom();
169        }
170
171        updatePlugin();
172        requestPaint();
173}
174
175void NeuroDiagram::showLive(Creature *_cr)
176{
177        showing_not_alive_label = 0;
178        if (!_cr) { show(0); return; }
179        show(&_cr->getModel());
180        cr = _cr;
181        livewire = true;
182        updatePlugin();
183        requestPaint();
184}
185
186void NeuroDiagram::paint()
187{
188        if (drawbackground)
189        {
190                setColor(ColorDefs::neurobackground);
191                clear();
192        }
193
194        if (countNeurons() > 0)
195        {
196                CanvasWindowContainer::paint();
197        }
198        else
199        {
200                setColor(ColorDefs::neuroneuron);
201                drawAlignedText(size.x / 2, (size.y - textHeight()) / 2, 0, "[No neural network]");
202        }
203
204        if (showing_not_alive_label)
205        {
206                if (time(0) > showing_not_alive_label)
207                        showing_not_alive_label = 0;
208                else
209                {
210                        setColor(0, 0, 0);
211                        drawAlignedText(not_alive_location.x, not_alive_location.y, 0, "select a creature");
212                        drawAlignedText(not_alive_location.x, not_alive_location.y + textHeight(), 0, "to probe neurons");
213                }
214        }
215}
216
217void NeuroDiagram::resize(int w, int h)
218{
219        CanvasWindowContainer::resize(w, h);
220        scroll.autoZoom();
221}
222
223int NeuroDiagram::countNeurons()
224{
225        return scroll.count();
226}
227
228void NeuroDiagram::addProbe(int i)
229{
230        if (i >= countNeurons()) return;
231        NeuroProbe *probe = new NeuroProbe(getNS(i));
232        Pixel s = getSize();
233        s.x = s.y = max(probe->getSize().x, min(s.x / 3, s.y / 3));
234        probes += (void*)probe;
235        add(probe);
236        probe->resizeClient(s);
237        updatePlugin();
238        requestPaint();
239}
240
241void NeuroDiagram::onKill(void*obj, intptr_t dummy)
242{
243        show(0);
244}
245
246///////////////////////////
247
248NeuroSymbol::NeuroSymbol(NeuroDiagram &nd, Neuro * _n)
249        :FramDrawToolkit(nd.getColorDefs()), selected(0), n(_n), diagram(nd)
250{
251        tooltip = "#";
252        tooltip += SString::valueOf((int)n->refno);
253        tooltip += " - ";
254        label = tooltip;
255        addNeuroDescription(tooltip, n);
256        label += n->getClassName();
257        if (n->getClassParams().length())
258        {
259                tooltip += "\n"; tooltip += n->getClassParams();
260        }
261}
262
263void NeuroSymbol::paint()
264{
265        if (selected)
266        {
267                setColor(ColorDefs::neuroselection);
268                fillRect(0, 0, size.x, size.y);
269        }
270        diagram.setClip();
271        diagram.setColor(ColorDefs::neuroneuron);
272        drawNeuroSymbol(this, n->getClass(), 0, 0, size.x, size.y);
273
274        if (size.y > 4 * textHeight())
275        {
276                const char* t = label.c_str();
277                setColor(ColorDefs::neurosymbol);
278                drawAlignedText(size.x / 2, size.y - textHeight(), 0, t);
279
280                NeuroImpl *ni = NeuroNetImpl::getImpl(n);
281                if (ni && (ni->getChannelCount() > 1))
282                {
283                        drawLine(size.x - size.x / 16, size.y / 2 - size.y / 16,
284                                size.x - size.x / 8, size.y / 2 + size.y / 16);
285                        char t[20];
286                        sprintf(t, "%d", ni->getChannelCount());
287                        moveTo(size.x, size.y / 2 - textHeight());
288                        drawText(t);
289                }
290        }
291
292        /*
293
294                                NeuroDiagram
295                                *........................................
296                                .                                       .
297                                .                    NeuroSymbol        .
298                                .      (pos.x,pos.y)-*.........         .
299                                .                    . |\     . ^  s    .
300                                .            ..._____._| \    . |  i    .
301                                .                    . |  \___. |  z    .
302                                .                  __._|  /   . |  e    .
303                                .                 |  . | /    . |  .    .
304                                .                 |  . |/     . |  y    .
305                                .                 |  .......... v       .
306                                .                 |  <-------->         .
307                                .      .......... |    size.x           .
308                                .      . |\     . |                     .
309                                .    __._| \    . |                     .
310                                .      . |  \___._|                     .
311                                .    __._|  /   .                       .
312                                .   |  . | /    .                       .
313                                .   |  . |/     .                       .
314                                .   |  ..........                       .
315                                .   |                                   .
316                                .   |________________...                .
317                                .........................................
318
319                                */
320
321                                // NeuroSymbol is also responsible for drawing connection lines from its inputs to other NeuroSymbols' outputs
322        NeuroSymbol *ns2;
323        if (!diagram.isLive())
324                diagram.setColor(ColorDefs::neurolink);
325        for (int iw = 0; iw < n->getInputCount(); iw++)
326        {
327                ns2 = diagram.getNS(n->getInput(iw)->refno); // the other NeuroSymbol (our input will connect to its output)
328
329                int yw = inputY(iw);
330                int xw = yw / 4; // x coordinate of the first corner point, depends on yw to avoid overlapping between inputs
331                drawLine(size.x / 4, yw, xw, yw); // first horizontal segment (to the left)
332                if (diagram.isLive())
333                        diagram.setWireColor(ns2->n->state, 0);
334                // linetype: 1 (default) - draw straight or U-shape depending on layout
335                //           0 (debug option) - only draw straight lines
336                if ((diagram.linetype != 1) || (ns2->pos.x + ns2->size.x / 2 < pos.x))
337                { // straight line to the other neuron's output (the signal goes forwards)
338                        ns2->lineTo(ns2->size.x, ns2->size.y / 2);
339                }
340                else
341                { // make an U-shaped loop from 3 segments - vert/horiz/vert (the signal goes backwards)
342                        int y2;
343                        int down;
344                        if (ns2 == this) down = (iw >= ((n->getInputCount()) / 2)); else down = (ns2->pos.y > (pos.y + size.y));
345                        if (down)
346                        {
347                                y2 = (pos.y + size.y + (size.y - yw) / 3);
348                        }
349                        else
350                        {
351                                y2 = pos.y - yw / 3;
352                                if ((ns2->pos.y < pos.y) && (ns2->pos.y > (pos.y - ns2->size.y))) y2 -= pos.y - ns2->pos.y;
353                        }
354                        // note: "diagram" uses global coordinate system, so we add "pos" or "ns2->pos" to get NeuroSymbol's global positions
355                        diagram.lineTo(pos.x + xw, y2);
356                        diagram.lineTo(ns2->pos.x + ns2->size.x, y2);
357                        diagram.lineTo(ns2->pos.x + ns2->size.x, ns2->pos.y + ns2->size.y / 2);
358                }
359
360        }
361}
362
363void NeuroSymbol::mouse(int x, int y, int t)
364{
365        if ((t & (LeftButton | ShiftButton)) == (LeftButton | ShiftButton))
366        {
367                ScrollManager& sc = diagram.scroll;
368                sc.setPos2(n->refno, pos.x + x - diagram.symboldragpos.x, pos.y + y - diagram.symboldragpos.y);
369                sc.validate();
370                requestPaint();
371        }
372}
373
374int NeuroSymbol::mouseclick(int x, int y, int t)
375{
376        if ((t & (LeftButton | DblClick)) == (LeftButton | DblClick))
377        {
378                if (diagram.isLive())
379                        diagram.addProbe(n->refno);
380                else
381                {
382                        if (diagram.warn_if_not_alive)
383                        {
384                                diagram.showing_not_alive_label = time(0) + 10;
385                                diagram.not_alive_location.x = pos.x + x;
386                                diagram.not_alive_location.y = pos.y + y;
387                                diagram.requestPaint();
388                        }
389                }
390                return LeftButton | DblClick;
391        }
392
393        if ((t & (LeftButton | ShiftButton)) == (LeftButton | ShiftButton))
394        {
395                if (selected)
396                        diagram.selection.remove(Model::neuroToMap(n->refno));
397                else
398                        diagram.selection.add(Model::neuroToMap(n->refno));
399                diagram.symboldragpos.set(x, y);
400                return LeftButton | ShiftButton;
401        }
402
403        if (t & LeftButton)
404        {
405                diagram.selection.set(Model::neuroToMap(n->refno));
406                return LeftButton;
407        }
408
409        return 0;
410}
411
412// coordinate y of i-th input
413int NeuroSymbol::inputY(int i)
414{
415        return (1 + i) * size.y / ((n->getInputCount()) + 1);
416}
417
418SString NeuroSymbol::hint(int x, int y)
419{
420        if ((y >= 0) && (y < size.y))
421                if (x < size.x / 4)
422                { // inputs?
423                        if (n->getInputCount() > 0)
424                        {
425                                int i = (y * n->getInputCount()) / size.y;
426                                double w;
427                                Neuro* target = n->getInput(i, w);
428                                if (target)
429                                {
430                                        SString t = "connected to #";
431                                        t += SString::valueOf((int)target->refno);
432                                        t += " - ";
433                                        addNeuroDescription(t, target);
434                                        //              if (w!=1.0)
435                                        {
436                                                t += ", weight=";
437                                                t += SString::valueOf(w);
438                                        }
439                                        return t;
440                                }
441                        }
442                }
443        return CanvasWindow::hint(x, y);
444}
445
446/////////////////////////
447
448NeuroProbe::NeuroProbe(NeuroSymbol* ns)
449        :DCanvasWindow(DCanvasWindow::Title + DCanvasWindow::Border + DCanvasWindow::Close + DCanvasWindow::Size,
450                ns->getLabel().c_str(), &neurochart, &neurochart)
451{
452        holdismine = 0;
453        drawing = 0; whichdrawing = -1;
454        clientbordersset = 0;
455        adjustingvalue = 0;
456        link = ns;
457        tooltip = SString("Probe for ") + ns->tooltip;
458        setPos(ns->getPos().x, ns->getPos().y);
459        neurochart.printMinMax(0);
460        neurochart.data.setMinMax(-1, 1);
461        chnum = 1; chnum2 = 0; chsel = 0;
462        chselwidth = 0;
463        chselecting = 0;
464        updateChannelCount(NeuroNetImpl::getImpl(link->n));
465}
466
467void NeuroProbe::onClose()
468{
469        link->diagram.probes -= this;
470        delete this;
471}
472
473NeuroProbe::~NeuroProbe()
474{
475        if (holdismine)
476                link->n->flags &= ~Neuro::HoldState;
477}
478
479void NeuroProbe::paint()
480{
481        static char t[40];
482        if (!clientbordersset)
483        {
484                clientbordersset = 1;
485                setClientBorder(0, 1, 16, textHeight() + 2); // don't use textheight outside paint/mouse events
486        }
487        int hold = link->n->flags & Neuro::HoldState;
488        float state = (float)link->n->state;
489        NeuroImpl *ni = 0;
490        if (chsel != 0)
491        {
492                ni = NeuroNetImpl::getImpl(link->n);
493                if (chsel < 0)
494                {
495                        int dr = -chsel - 1;
496                        if (whichdrawing != dr)
497                        {
498                                drawing = ni->getDrawing(dr);
499                                whichdrawing = dr;
500                        }
501                        if (drawing)
502                        {
503                                int *dr = drawing;
504                                int w = size.x - 2, h = size.y - clienttop - clientbottom;
505                                int scale = min(w, h);
506                                int x0 = clienttop + leftborder + ((w > h) ? (w - h) / 2 : 0);
507                                int y0 = clientleft + topborder + ((h > w) ? (h - w) / 2 : 0);
508
509                                while (*dr != NeuroImpl::ENDDRAWING)
510                                {
511                                        int first = 1;
512                                        unsigned char r, g, b;
513                                        FramDrawToolkit::splitRGB(*(dr++), r, g, b);
514                                        setColor(r, g, b);
515                                        while (*dr != NeuroImpl::ENDDRAWING)
516                                        {
517                                                int x = ((*(dr++)) * scale) / (NeuroImpl::MAXDRAWINGXY + 1) + x0;
518                                                int y = ((*(dr++)) * scale) / (NeuroImpl::MAXDRAWINGXY + 1) + y0;
519                                                if (first) { moveTo(x, y); first = 0; }
520                                                else lineTo(x, y);
521                                        }
522                                        dr++;
523                                }
524                        }
525                }
526        }
527        DCanvasWindow::paintWithClient((chsel < 0) ? 0 : client);
528        setColor(0, 0, 0);
529        int yline = size.y - 2;
530        if (chsel >= 0)
531        {
532                if (ni) state = (float)ni->getState(chsel);
533                yline -= textHeight();
534                int y = mapClientY(neurochart.mapData(state));
535                int x = size.x - 15 - 1;
536                drawLine(1, yline, size.x - 2, yline);
537                if (hold)
538                {
539                        sprintf(t, "hold: %1.3g", state);
540                        fillRect(x, y - 1 - 5, 15, 3 + 5 + 5);
541                        setColor(255, 0, 0);
542                        fillRect(x + 2, y - 1, 15 - 2 - 2, 3);
543                }
544                else
545                {
546                        sprintf(t, "signal: %1.3g", state);
547                        fillRect(x, y - 1, 15, 3);
548                }
549                drawAlignedText(size.x - textHeight(), yline, 1, t);
550        }
551
552        if ((chnum > 1) || (chnum2 > 0))
553        {
554                if (chselecting) setColor(255, 255, 255); else setColor(0, 70, 0);
555                if (chsel < 0)
556                        sprintf(t, "%c/%c", 'A' - chsel - 1, 'A' + chnum2 - 1);
557                else
558                        sprintf(t, "%d/%d", chsel, chnum);
559                moveTo(0, yline - textHeight());
560                chselwidth = textWidth(t);
561                drawText(t, -1, getSize().x);
562        }
563        else
564                chselwidth = 0;
565}
566
567void NeuroProbe::mouse(int x, int y, int b)
568{
569        if (chselecting)
570        {
571                int ch = chsel0 + (x - chselx0) / 10;
572                if (selectChannel(ch)) requestPaint();
573                b &= ~LeftButton;
574        }
575        DCanvasWindow::mouse(x, y, b);
576        if (adjustingvalue)
577        {
578                double st = neurochart.unmapData(unmapClientY(y));
579                if (st < -1.0) st = -1.0; else if (st > 1.0) st = 1.0;
580                if (chsel == 0)
581                        link->n->state = st;
582                else if (chsel >= 0)
583                {
584                        NeuroImpl *ni = NeuroNetImpl::getImpl(link->n);
585                        if (ni) ni->setCurrentState(st, chsel);
586                }
587                requestPaint();
588        }
589}
590
591void NeuroProbe::mouseunclick(int x, int y, int b)
592{
593        adjustingvalue = 0;
594        chselecting = 0;
595        DCanvasWindow::mouseunclick(x, y, b);
596}
597
598bool NeuroProbe::insideChSelector(int x, int y)
599{
600        if ((x > 0) && (x < chselwidth))
601        {
602                int sy = size.y;
603                if (chsel >= 0) sy -= textHeight();
604                return ((y < sy) && (y > (sy - textHeight())));
605        }
606        return 0;
607}
608
609int NeuroProbe::mouseclick(int x, int y, int b)
610{
611        if ((b & LeftButton) && insideChSelector(x, y))
612        {
613                chselx0 = x; chsel0 = chsel;
614                chselecting = 1;
615                requestPaint();
616                return LeftButton;
617        }
618        int ret = DCanvasWindow::mouseclick(x, y, b);
619        if (ret)
620        {
621                link->diagram.selection.set(Model::neuroToMap(link->n->refno));
622                return ret;
623        }
624        if (b & LeftButton)
625        {
626                if (x > size.x - 16)
627                {
628                        link->n->flags |= Neuro::HoldState;
629                        holdismine = 1;
630                        adjustingvalue = 1;
631                        mouse(x, y, b);
632                        return LeftButton;
633                }
634                else if (y > size.y - 16)
635                {
636                        link->n->flags ^= Neuro::HoldState;
637                        holdismine = ((link->n->flags & Neuro::HoldState) != 0);
638                        requestPaint();
639                        return LeftButton;
640                }
641        }
642        return 0;
643}
644
645SString NeuroProbe::hint(int x, int y)
646{
647        if ((chsel >= 0) && (x < size.x - 16) && (y > size.y - 16))
648                return SString((link->n->flags & Neuro::HoldState) ? "Click to release" : "Click to hold");
649        else if (insideChSelector(x, y))
650                return SString::sprintf("channel %d of %d (click and drag to switch channels)", chsel, chnum);
651        return DCanvasWindow::hint(x, y);
652}
653
654/** @return true == channel changed */
655bool NeuroProbe::selectChannel(int ch)
656{
657        if (ch < -chnum2) ch = -chnum2; else if (ch >= chnum) ch = chnum - 1;
658        if (ch == chsel) return false;
659        chsel = ch;
660        neurochart.data.clear();
661        return true;
662}
663
664void NeuroProbe::updateChannelCount(NeuroImpl *ni)
665{
666        if (!ni) return;
667        chnum = ni->getChannelCount();
668        chnum2 = ni->getDrawingCount();
669        if (chsel >= chnum) selectChannel(chnum - 1);
670        if (chsel < -chnum2) selectChannel(-chnum2);
671}
672
673void NeuroProbe::sampling()
674{
675        NeuroImpl *ni = NeuroNetImpl::getImpl(link->n);
676        updateChannelCount(ni);
677        if (!chsel)
678                neurochart.data += (float)(link->n->state);
679        else
680                neurochart.data += (float)(ni->getState(chsel));
681        whichdrawing = -1;
682}
683
684////
685
686void NeuroDiagram::probeSampling(void*obj, intptr_t dummy)
687{
688        FOREACH(NeuroProbe*, pr, probes) pr->sampling();
689        requestPaint();
690}
691
692void NeuroDiagram::updatePlugin()
693{
694        //int needplugin=(!probes)>0;
695        bool needplugin = livewire;
696        if (needplugin == pluginactive) return;
697        if (needplugin)
698        {
699                if (!cr) return;
700                sim = cr->group->getLibrary().sim;
701                pluginnode = sim->l_plugin.add(STATRICKCALLBACK(this, &NeuroDiagram::probeSampling, 0));
702        }
703        else
704                sim->l_plugin.remove(pluginnode);
705        pluginactive = needplugin;
706}
707
708/////////////
709
710void NeuroDiagramSelection::updateSelection(MultiRange& newsel)
711{
712        MultiRange added = getAdded(newsel);
713        if (!added.isEmpty())
714        {
715                added.shift(Model::mapToNeuro(0));
716                added.intersect(0, diagram.countNeurons() - 1);
717                for (int i = 0; i < added.rangeCount(); i++)
718                {
719                        const IRange &r = added.getRange(i);
720                        for (int j = r.begin; j <= r.end; j++)
721                                diagram.getNS(j)->selected = 1;
722                }
723        }
724        MultiRange removed = getRemoved(newsel);
725        if (!removed.isEmpty())
726        {
727                removed.shift(Model::mapToNeuro(0));
728                removed.intersect(0, diagram.countNeurons() - 1);
729                for (int i = 0; i < removed.rangeCount(); i++)
730                {
731                        const IRange &r = removed.getRange(i);
732                        for (int j = r.begin; j <= r.end; j++)
733                                diagram.getNS(j)->selected = 0;
734                }
735        }
736        if (!diagram.indestructor) diagram.requestPaint();
737}
Note: See TracBrowser for help on using the repository browser.