//====================  DISTANCE MAPPING STUFF  ====================//

        private void ComputeDistanceMaps(BlobData gt)
        {
            int CORE = 0;
            int NEW  = -1;

            var gtpl = gt.pixels.Length;

            gt.distOut = new int[gtpl];
            gt.distIn  = new int[gtpl];
            for (int i = 0; i < gtpl; i++)
            {
                var gtm = gt.pixels[i] != 0;
                gt.distOut[i] = gtm ? CORE : NEW;
                gt.distIn[i]  = gtm ? NEW : CORE;
            }

            var w = _canvas.width;

            RemapFlaggedIntArrayAsDepth(ref gt.distOut, w, 0, distanceFactor);
            RemapFlaggedIntArrayAsDepth(ref gt.distIn, w, 0, distanceFactor);
        }
        private void ContinuePaint(Vector2 position)
        {
            // Check to make sure last texture coordinate is set to a reasonable value.
            if (_lastTexCoord.x == -50000 || _lastTexCoord.y == -50000)
            {
                return;
            }

            var positions = new List <Vector2>();
            var gap       = Vector2.Distance(_lastTexCoord, position) * (_canvas.width + _canvas.height) * 0.5f;

            if (gap > minGap)
            {
                int times = Mathf.FloorToInt(gap / minGap);

                // Increment only the maximum number of times for gaps.
                if (times > maxGapIncrements)
                {
                    times = maxGapIncrements;
                }

                for (float i = 1; i < times; i++)
                {
                    positions.Add(Vector2.Lerp(_lastTexCoord, position, i / times));
                }
            }
            positions.Add(position);

            var      ci              = GetCurrentIndex();
            BlobData userBlob        = GetUserBlobForIndex(ci);
            var      affectedIndices = new List <int>();

            foreach (var pos in positions)
            {
                foreach (var ai in DoStrokeAt(pos, ci, userBlob))
                {
                    if (!affectedIndices.Contains(ai))
                    {
                        affectedIndices.Add(ai);
                    }
                }
            }
            _canvas.Apply();

            var userMarkups = new List <MarkupBase>();

            foreach (var index in affectedIndices)
            {
                var affBlob = GetUserBlobForIndex(index);
                affBlob.center = GetWeightedBlobCenter(ref affBlob.pixels, _canvas.width);
                if (affBlob.center.x < -49000)
                {
                    _userBlobs.Remove(index);
                }
                else
                {
                    userMarkups.Add(affBlob);
                }
            }

            if (!mystery)
            {
                UpdateMarkupPercents(userMarkups);
            }
        }
        private List <int> DoStrokeAt(Vector2 position, int ci, BlobData userBlob)
        {
            HashSet <int> ret = new HashSet <int>();

            if (!_painting && !_erasing)
            {
                return(ret.ToList());
            }
            if (!_erasing)
            {
                ret.Add(ci);
            }

            int   cw = _canvas.width;
            int   ch = _canvas.height;
            float px = position.x * cw;
            float py = position.y * ch;

            int rw = Mathf.FloorToInt(_radius * cw);
            int rh = Mathf.FloorToInt(_radius * ch);
            int cx = Mathf.FloorToInt(px - rw * 0.5f);
            int cy = Mathf.FloorToInt(py + rh * 0.5f);

            float avgR = (Mathf.Max(rw, rh)) / 4f;

            var color = _color;

            if (!_erasing)
            {
                var colorWiggle = UIMgr.Instance.colorWiggle;
                color = color - new Color(UnityEngine.Random.Range(0, colorWiggle), UnityEngine.Random.Range(0, colorWiggle), UnityEngine.Random.Range(0, colorWiggle), 0);
            }

            var avgRSquared = avgR * avgR;

            // TODO: convert this to run on getpixels/setpixels jfc lol
            int l = _indices.Length;

            for (int x = cx; x < cx + rw; x++)
            {
                for (int y = cy; y >= cy - rh; y--)
                {
                    // Faster to compare squared distances.
                    if ((px - x) * (px - x) + (py - y) * (py - y) < avgRSquared)
                    {
                        _canvas.SetPixel(x, y, color);
                        var pos = y * cw + x;
                        if (pos >= 0 && pos < l)
                        {
                            var old = _indices[pos];
                            if (old > 0)
                            {
                                if (_erasing && _tempOld < 0)
                                {
                                    _tempOld = old;
                                }
                                GetUserBlobForIndex(old).pixels[pos] = 0;
                                // Can add at will since hashset by default does not allow for duplicates.
                                ret.Add(old);
                            }

                            _indices[pos] = ci;
                            if (_painting && userBlob != null)
                            {
                                userBlob.pixels[pos] = ci;
                            }
                        }
                    }
                }
            }

            return(ret.ToList());
        }
        override protected IEnumerator ParseGroundTruths()
        {
            var groundTruthTex2D = GameMgr.CurrentGroundTruthTex2D();

            if (groundTruthTex2D == null)
            {
                Debug.LogError("no image for ground truth generation"); yield break;
            }

            //compute is slow, this updates app status first
            DebugText.instance.Log("Generating distance maps...");
            yield return(null);

            _groundTruthBlobs = new Dictionary <int, BlobData>();

            //pre-process image to extract indices from the pixels
            var gt = groundTruthTex2D;

            if (gt == null)
            {
                DebugText.instance.Log("ERROR: could not find ground truth"); yield break;
            }
            var gtPixels  = gt.GetPixels32();
            var gp        = gt.width * gt.height;
            var gtIndices = new int[gp];

            for (int i = 0; i < gp; i++)
            {
                gtIndices[i] = gtPixels[i].r;
            }

            int blobIndex = 0;
            int safety    = 0;
            int rolling   = 0;

            while (safety++ < 500)
            {
                //get the first non-zero index
                var pos = -1;
                for (; rolling < gp; rolling++)
                {
                    if (gtIndices[rolling] > 0)
                    {
                        pos = rolling;
                        break;
                    }
                }
                if (pos < 0)
                {
                    DebugText.instance.Log("SUCCESS: loaded ground truth"); break;
                }

                // Get all connected indices
                var index = gtIndices[pos];
                if (index <= 0)
                {
                    DebugText.instance.Log("ERROR: problem loading ground truth"); break;
                }

                // Just build a mask by scanning the image, don't lasso at all
                BlobData blob = new BlobData(gt.width, gt.height, index);
                for (int i = 0; i < blob.pixels.Length; i++)
                {
                    if (gtIndices[i] == index)
                    {
                        blob.pixels[i] = index;
                        gtIndices[i]   = 0;
                    }
                    else
                    {
                        blob.pixels[i] = 0;
                    }
                }

                blob.Resize();
                blobIndex++;

                blob.center = GetWeightedBlobCenter(ref blob.pixels, gt.width);
                ComputeDistanceMaps(blob);
                _groundTruthBlobs.Add(index, blob);
            }
        }