Nearest Yield ⚠️

Some experimenting with Yield, NearestNeighbors while maintaining walk(i);

Nearest Yield ⚠️ (variation)

Visual idea from a plotterfile by @msurguy at plotterfiles.com/@ms…2f-b64c-c0f665a70cff

(the plotterfiles.com link does not work as TurtleToy replaces the @ with @ which breaks the site)

Log in to post a comment.

// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(1);

// Global code will be evaluated once.
const turtle = new Turtle();
const randomSeed = 0;     // min=0 max=1000 step=1
const radius = 50;  // min=10 max=90 step=1    
const tooFarThresholdRt = 50; //min=10 max=1000 step=1 
const continuousDraw = 0;     // min=0 max=1 step=1 (No,Yes)
const rings = 3; //min=0 max=10 step=1

// Seedable random number generator by David Bau: http://davidbau.com/archives/2010/01/30/random_seeds_coded_hints_and_quintillions.html
!function(a,b,c,d,e,f,g,h,i){function j(a){var b,c=a.length,e=this,f=0,g=e.i=e.j=0,h=e.S=[];for(c||(a=[c++]);d>f;)h[f]=f++;for(f=0;d>f;f++)h[f]=h[g=s&g+a[f%c]+(b=h[f])],h[g]=b;(e.g=function(a){for(var b,c=0,f=e.i,g=e.j,h=e.S;a--;)b=h[f=s&f+1],c=c*d+h[s&(h[f]=h[g=s&g+b])+(h[g]=b)];return e.i=f,e.j=g,c})(d)}function k(a,b){var c,d=[],e=typeof a;if(b&&"object"==e)for(c in a)try{d.push(k(a[c],b-1))}catch(f){}return d.length?d:"string"==e?a:a+"\0"}function l(a,b){for(var c,d=a+"",e=0;e<d.length;)b[s&e]=s&(c^=19*b[s&e])+d.charCodeAt(e++);return n(b)}function m(c){try{return o?n(o.randomBytes(d)):(a.crypto.getRandomValues(c=new Uint8Array(d)),n(c))}catch(e){return[+new Date,a,(c=a.navigator)&&c.plugins,a.screen,n(b)]}}function n(a){return String.fromCharCode.apply(0,a)}var o,p=c.pow(d,e),q=c.pow(2,f),r=2*q,s=d-1,t=c["seed"+i]=function(a,f,g){var h=[];f=1==f?{entropy:!0}:f||{};var o=l(k(f.entropy?[a,n(b)]:null==a?m():a,3),h),s=new j(h);return l(n(s.S),b),(f.pass||g||function(a,b,d){return d?(c[i]=a,b):a})(function(){for(var a=s.g(e),b=p,c=0;q>a;)a=(a+c)*d,b*=d,c=s.g(1);for(;a>=r;)a/=2,b/=2,c>>>=1;return(a+c)/b},o,"global"in f?f.global:this==c)};if(l(c[i](),b),g&&g.exports){g.exports=t;try{o=require("crypto")}catch(u){}}else h&&h.amd&&h(function(){return t})}(this,[],Math,256,6,52,"object"==typeof module&&module,"function"==typeof define&&define,"random");
Math.seedrandom('' + randomSeed);

function distanceSquared(p1, p2) {
    return Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2);
}

class NearestNeighbor {
    points = [];
    pointCount = 0;
    constructor(pointCount) {
        this.pointCount = pointCount;
    }
    
    init() {
        for(let pt of this.generatePoint()) {
            this.points.push(pt);
        }
        
        let index = this.nearestUnvisited([0, 0]);

        while(index !== null) {
            let found = this.nearestUnvisited(this.points[index]);
            this.points[index][2] = found;
            index = found;
        }
        
        return this;
    }
    
    nearestUnvisited(coord) {
        let nearest = [null, 10000];
        for(let i = 0; i < this.points.length; i++) {
            if(this.points[i][2] !== false) {
                continue;
            }
            
            let d = distanceSquared(this.points[i], coord);

            if(d < nearest[1]) {
                nearest = [i, d];
            }
        }
        return nearest[0];
    }
    
    *iteratePoints() {
        for(let i = 0; i < this.points.length; i++) {
            yield this.points[i];
        }
    }
    
    getPoint(index) {
        return this.points[index];
    }
}

class NearestNeighborSphere extends NearestNeighbor {
    constructor(pointCount, radius) {
        super(pointCount);
        this.R = radius;
    }
    *generatePoint () {
        for(let i = 0; i < this.pointCount; i++) {
            let r = this.R * Math.sin(Math.sqrt(Math.random()) * Math.PI / 2);
            let theta = Math.random() * 2 * Math.PI;

            yield [r * Math.cos(theta), r * Math.sin(theta), false];
        }
    }
}

class NearestNeighborRing extends NearestNeighborSphere {
    *generatePoint () {
        for(let i = 0; i < this.pointCount; i++) {
            let theta = Math.random() * 2 * Math.PI;

            yield [this.R * Math.cos(theta), this.R * Math.sin(theta), false];
        }
    }
}

class NearestNeighborIterator {
    nns = [];
    currentNN = null;
    add(nn) {
        this.nns.push(nn);
    }
    getNN() {
        return this.currentNN;
    }
    *points() {
        for(let nn of this.nns) {
            this.currentNN = nn;
            for(let pt of nn.iteratePoints()) {
                yield pt;
            }
        }
        this.currentNN = null;
    }
}

let nni = new NearestNeighborIterator();
nni.add(new NearestNeighborSphere(Math.pow(radius, 2.1), radius).init());
for(let i = 0; i < rings; i++) {
    nni.add(new NearestNeighborRing(Math.pow(radius + (radius * ((i + 1)/(rings + 2))), 2) / 16, radius + (radius * ((i + 1)/(rings + 2)))).init());
}

let drawToggler = false;
let iterator = nni.points();

// The walk function will be called until it returns false.
function walk(i) {
    let pt = iterator.next().value;

    if(pt !== undefined) {
        if(nni.getNN().points[pt[2]] !== undefined) {
            turtle.jump(pt);
            if((continuousDraw === 1 || !drawToggler) && distanceSquared(pt, nni.getNN().points[pt[2]]) < tooFarThresholdRt) {
                turtle.goto(nni.getNN().points[pt[2]])
                drawToggler = true;
            } else {
                turtle.jump(nni.getNN().points[pt[2]])
                drawToggler = false;
            }
        }
        return true;
    }
    return false;
}