Interconnected nodes

I wrote this script specifically for my Systems, math and explosions (in no particular order) blog post. The script generates networks of nodes based on the configuration you give it. There’s really no function to this script beyond drawing pretty pictures.

Instructions

This script is intended to run within Figma through its plugin API. The fastest way to run this script is by copy-pasting within Scripter.

            

// The size of each node "dot"
const DOT_SIZE = 2;
// The number of nodes that splines will interconnect to
const SIDES = 12;
// The line width of each spline interconnecting each node
const LINE_WEIGHT = .25;
// The level of line width randomness for each spline 
// TODO: FIX
const LINE_WEIGHT_JITTER = 0;
// The opacity of each spline interconnecting each node
const LINE_OPACITY = .5;
// TODO: REMOVE
const POLY_RADIUS = 124;
// The multiplier that sets the "bendiness" of each spline
const BEZIER_MULTIPLIER = function(distance) { return Math.sqrt(distance) / 30 };
// The percentage that a line shows up. 1 = 100%, 0 = 0%
const LINE_DISPLAY_PROBABILITY = 1;
// The amount of degrees to rotate the polygon
const ROTATIONAL_OFFSET = 30;

const BACKGROUND = {r:1, g:1, b: 1};
const FOREGROUND = {r:0, g: 0, b:0};

const FRAME_SIZE = 400;

const FILL_SEED = [{
    type: "SOLID",
    visible: true,
    opacity: 1,
    blendMode: "NORMAL",
    color: {
      r: 1,
      g: 1,
      b: 1
    },
    boundVariables: {}
  }]

var drawPoly = function(frame, sides, size, offset = 0) {
	let midX = FRAME_SIZE/2;
	let midY = FRAME_SIZE/2;
	if(!offset) offset = 0;
	var dot;
	var fill;

	for (var i = 1; i <= sides;i += 1) {
		dot = figma.createEllipse();
		fill = clone(dot.fills)

		fill[0].color.r=1;
		fill[0].color.g=1;
		fill[0].color.b=1;
		//fill.opacity=0;

		dot.resize(DOT_SIZE, DOT_SIZE);
		dot.x = midX + size * Math.cos((offset * Math.PI/180) +i * 2 * Math.PI / sides) - DOT_SIZE/2
		dot.y = midY + size * Math.sin((offset * Math.PI/180) +  i * 2 * Math.PI / sides) - DOT_SIZE/2
		dot.fills = foregroundFill;
		frame.appendChild(dot)
	}	
} 

function clone(val) {
  return JSON.parse(JSON.stringify(val))
}

var makeLine = function(frame, start, mid, end) {
	var vector = figma.createVector();
	
	vector.strokeWeight = LINE_WEIGHT + (Math.random()* LINE_WEIGHT_JITTER * (Math.random() < 0.5 ? -1 : 1));

	var distX = Math.abs(end.x - start.x);
	var distY = Math.abs(end.y - start.y);
	var dist = Math.sqrt(Math.pow(distX, 2) + Math.pow(distY, 2));

	var mult = BEZIER_MULTIPLIER(dist);
		
	vector.vectorNetwork = {
		vertices: [
			start,
			end,
		],

		segments: [
			{
				start: 0,
				tangentStart: { x: (FRAME_SIZE/2 - start.x) * mult , y: (FRAME_SIZE/2 - start.y) * mult }, 
				end: 1,
				tangentEnd: { x: (FRAME_SIZE/2 - end.x) * mult, y: (FRAME_SIZE/2 - end.y) * mult },
			}
		],
	}
	
	var strokes = clone(vector.strokes)

	strokes[0].opacity = LINE_OPACITY;
	strokes[0].color.r=FOREGROUND.r;
	strokes[0].color.g=FOREGROUND.g;
	strokes[0].color.b=FOREGROUND.b;

	vector.strokes = strokes
	vector.strokeCap = "ROUND";
	frame.appendChild(vector)
}

var frame = figma.createFrame();
frame.name = "Splines: " + SIDES + ' @ 400px'
frame.resize(FRAME_SIZE, FRAME_SIZE);
figma.currentPage.appendChild(frame);

var frameFill = FILL_SEED;
frameFill[0].color.r = BACKGROUND.r
frameFill[0].color.g = BACKGROUND.g
frameFill[0].color.b = BACKGROUND.b

frame.fills = frameFill;

var foregroundFill = FILL_SEED
foregroundFill[0].color.r = FOREGROUND.r
foregroundFill[0].color.g = FOREGROUND.g
foregroundFill[0].color.b = FOREGROUND.b

drawPoly(frame, SIDES, POLY_RADIUS, ROTATIONAL_OFFSET)

var children = frame.children;
for(var i = 0; i < children.length; i++) {
	var start = {x:children[i].x + children[i].width/2, y:children[i].y + children[i].height/2}
	for(var j = i + 1; j < children.length; j++) {
		if(i !== j ) {
			var end = {x:children[j].x + children[j].width/2, y:children[j].y + children[j].height/2}
			if(Math.random() < LINE_DISPLAY_PROBABILITY) {
				makeLine(frame, start, {x:frame.width/2, y:frame.height/2}, end)
			}
			
		}
	}
}