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

Last change on this file since 1319 was 1216, checked in by Maciej Komosinski, 20 months ago

Cosmetic

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