# Pattern Attractors

**URL:** <https://discourse.processing.org/t/pattern-attractors/2535>\
**Category:** Project Guidance\
**Created:** [August 10, 2018, 4:13pm UTC](https://discourse.processing.org/t/pattern-attractors/2535 "2018-08-10T16:13:02Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![jakejake](https://avatars.discourse-cdn.com/v4/letter/j/e495f1/32.png) [@jakejake](https://discourse.processing.org/u/jakejake)\
**Post date:** [August 10, 2018, 4:13pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/1 "2018-08-10T16:13:02Z")

</div>

![72eb5064ff6eb2cbf308bd4bee910680--grasshopper-facade-grasshopper-rhino](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/e/ed5c4abbde105c4e3e3440872cf6186c91a0f382.jpeg)

Trying figure out how to get this functionality with processing.

I know how to map distance from a single point to something like a circle width / height, but I have no idea how to tackle this.

Is there an example script for something similar? I think grasshopper calls them curve attractors, but I’m not getting any results searching that for processing.

---

<div class="post-metadata">

**Author:** ![jb4x](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/jb4x/32/789_2.png) [@jb4x](https://discourse.processing.org/u/jb4x)\
**Post date:** [August 10, 2018, 4:31pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/2 "2018-08-10T16:31:40Z")

</div>

Hey!

Right now it is hard to help you since what you try to achieve is not pretty clear.  
Do you have a concrete example of what you would like to be able to do?

From what I get there are this is how a pattern attractor works:

- You first take a polygon
- Then you subdivide it in cells
- Then you have a curve that will be used as the attractor
- For every cells you find the smallest distance to the curve
- With that value, you can now change some properties of the cell

---

<div class="post-metadata">

**Author:** ![jakejake](https://avatars.discourse-cdn.com/v4/letter/j/e495f1/32.png) [@jakejake](https://discourse.processing.org/u/jakejake)\
**Post date:** [August 10, 2018, 6:49pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/3 "2018-08-10T18:49:23Z")

</div>

![72eb5064ff6eb2cbf308bd4bee910680--grasshopper-facade-grasshopper-rhino](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/e/ed5c4abbde105c4e3e3440872cf6186c91a0f382.jpeg)

this is the image

---

<div class="post-metadata">

**Author:** ![jb4x](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/jb4x/32/789_2.png) [@jb4x](https://discourse.processing.org/u/jb4x)\
**Post date:** [August 10, 2018, 6:51pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/4 "2018-08-10T18:51:35Z")

</div>

What is your problem then, can you be more specific?

---

<div class="post-metadata">

**Author:** ![behreajj](https://avatars.discourse-cdn.com/v4/letter/b/b5a626/32.png) [@behreajj](https://discourse.processing.org/u/behreajj)\
**Post date:** [August 13, 2018, 10:30am UTC](https://discourse.processing.org/t/pattern-attractors/2535/5 "2018-08-13T10:30:25Z")

</div>

Hi jakejake,

This looked neat, so I gave it a go here.

> **[Curve Attractor - OpenProcessing](https://www.openprocessing.org/sketch/579648)**
>
> Based on curve attractors in Grasshopper.

I know nothing about Grasshopper, but this [Generative Landscapes tutorial](https://generativelandscapes.wordpress.com/2014/08/14/curve-attractors-example-2-3/) laid out the process fairly well. Between that and the steps @jb4x laid out, I had a path forward. To repeat and rephrase what’s already been said:

- I took an ideal grid of the range ([0 - 1], [0 - 1]).

- I subdivided that range by a count, storing the result in an array of vectors.

- I used a sine function to generated an array of vectors.

- How did I know which distance from the cell to the curve is smallest? Here’s why the sketch is slow, and where there may be room to improve.

- The distance info needs to be compared against a maximum possible distance to be converted to a range [0 - 1]. Once that’s through, I can supply that range to `lerp` or `lerpColor`, etc.

Sorting an array by a custom criterion requires a bit more gear in Java, but the idea remains the same.

```auto
import java.util.Comparator;
import java.util.Arrays;

static void drawEllipses(PGraphics r, 
  PVector[] grid, PVector[] curve, 
  float left, float right, 
  float bottom, float top, 
  float padding, float minScale, 
  color clr0, color clr1, int clrMode, 
  DistSort sorter) {
  /* ... */
  for (int i = 0; i < len; ++i) {
    /* ... */
    Arrays.sort(curve, sorter);
    /* ... */
  }
  /* ... */
}

static class DistSort implements Comparator < PVector > {
  /* ... */
  int compare(PVector a, PVector b) {
     /* Return either -1, 0, or 1. */
  }
}

```

Hope that helps out. Best.

---

<div class="post-metadata">

**Author:** ![tony](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/tony/32/949_2.png) [@tony](https://discourse.processing.org/u/tony)\
**Post date:** [August 13, 2018, 4:14pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/6 "2018-08-13T16:14:41Z")

</div>

If I understand correctly, what you’re saying is you know how to find the distance of a point from say a circle, but you don’t know how to find a point on a curve.

If that’s the case, I can point you towards Bezier curves, which are really neat! (And which are built into Processing with [bezier()](https://processing.org/reference/bezier_.html))

If you have any more questions, just ask.

---

<div class="post-metadata">

**Author:** ![tony](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/tony/32/949_2.png) [@tony](https://discourse.processing.org/u/tony)\
**Post date:** [August 13, 2018, 4:18pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/7 "2018-08-13T16:18:05Z")

</div>

> [@jb4x](#):
>
> For every cells you find the smallest distance to the curve

Haha, I’ve actually been playing around with bezier curves recently and this is a LOT harder than I thought it would be.

[Be gentle, it’s not finished](https://www.openprocessing.org/sketch/577977)

---

<div class="post-metadata">

**Author:** ![jakejake](https://avatars.discourse-cdn.com/v4/letter/j/e495f1/32.png) [@jakejake](https://discourse.processing.org/u/jakejake)\
**Post date:** [August 13, 2018, 5:15pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/8 "2018-08-13T17:15:47Z")

</div>

Thanks for the replys guys! sorry as a new user I got locked out for a little bit.

jb4x, your breakdown helped me wrap my head around the concept. It sounds like with attractor shape will need to be represented by xy points, where distance is checked for each point in the grid.

---

<div class="post-metadata">

**Author:** ![jakejake](https://avatars.discourse-cdn.com/v4/letter/j/e495f1/32.png) [@jakejake](https://discourse.processing.org/u/jakejake)\
**Post date:** [August 13, 2018, 5:19pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/9 "2018-08-13T17:19:30Z")

</div>

Yes tony, I wasn’t sure how to check distance from a curve / shape, but it seems like checking an array of xy coordinates is the best way to go. I could probably do a pshape or import an svg with geomerative and use the polygonizer too.

The only success I have had was blurring shapes in photoshop and mapping the brightness of each coordinate to a circle size which is easy to code, but not ideal.

---

<div class="post-metadata">

**Author:** ![jakejake](https://avatars.discourse-cdn.com/v4/letter/j/e495f1/32.png) [@jakejake](https://discourse.processing.org/u/jakejake)\
**Post date:** [August 13, 2018, 5:21pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/10 "2018-08-13T17:21:43Z")

</div>

thanks for making this @behreajj! I appreciate the notes as well, I will have to study this a bit to understand whats going on, but that is the idea of what I’m trying to achieve.

---

<div class="post-metadata">

**Author:** ![tony](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/tony/32/949_2.png) [@tony](https://discourse.processing.org/u/tony)\
**Post date:** [August 13, 2018, 7:03pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/11 "2018-08-13T19:03:51Z")

</div>

One approach is to take a small number of points along the curve to reduce the calculation of distance. You’d probably have to run some tests to find out the optimal balance of points vs perfermance.

---

<div class="post-metadata">

**Author:** ![jb4x](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/jb4x/32/789_2.png) [@jb4x](https://discourse.processing.org/u/jb4x)\
**Post date:** [August 13, 2018, 7:47pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/12 "2018-08-13T19:47:16Z")

</div>

As @tony said, finding the shortest distance from a point to a bezier curve is not that trivial.

One way one can approach would be the following.

First let’s consider

- P0(x0,y0)
- P1(x1,y1)
- P2(x2,y2)
- P3(x3,y3)

the control points of the bezier curve.

The curve is then defined by all the points P(t) for `0 <= t <= 1` as followed:  
`P(t) = P0*(1-t)^3 + 3*P1*t*(1-t)^2+3*P2*t^2*(1-t)+P3*t^3`

Now let’s consider a point `D(x, y)`  
The distance from D to the bezier curve is: `min(for all P of the curve) ||D-P||`

I am not developing the formula here because it would be too long but it is easy to get `||D-P||²` with all the parameters given above.

It would be feasible to get `d(||D-P||²)/dt` and then use a [root finding algorithm](https://en.wikipedia.org/wiki/Root-finding_algorithm) to get the point were the smallest distance is.

Hope it can help 🙂

PS: Is there a way to properly display math formulas?

---

<div class="post-metadata">

**Author:** ![tony](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/tony/32/949_2.png) [@tony](https://discourse.processing.org/u/tony)\
**Post date:** [August 13, 2018, 8:13pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/13 "2018-08-13T20:13:21Z")

</div>

> [@jb4x](#):
>
> P(t) = P0\*(1-t)^3 + 3_P1_t\*(1-t)^2+3_P2_t^2\*(1-t)+P3\*t^3

Luckily, Processing also offers the [bezierPoint()](https://processing.org/reference/bezierPoint_.html) function.

Before you use this I would definitely do some reading on bezier curves. Though they are a little daunting at first, the main thing you need to understand is how control points relate to the curve.

Woah, another shameless plug to a [bezier curve maker](https://www.openprocessing.org/sketch/571543) ! Play around with this sketch. Control points and anchors sound confusing, but seeing how you can manipulate a curve with them, hands on, is pretty intuitive.

---

<div class="post-metadata">

**Author:** ![jb4x](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/jb4x/32/789_2.png) [@jb4x](https://discourse.processing.org/u/jb4x)\
**Post date:** [August 13, 2018, 8:33pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/14 "2018-08-13T20:33:36Z")

</div>

> [@tony](#):
>
> Luckily, Processing also offers the [bezierPoint()](https://processing.org/reference/bezierPoint_.html) function.

That’s a pretty cool function, it can be really useful!

> [@tony](#):
>
> seeing how you can manipulate a curve with them, hands on, is pretty intuitive.

This is one of the reason why they became so famous. That and the fact that you can store a whole curve with just 4 points!

---

<div class="post-metadata">

**Author:** ![jb4x](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/jb4x/32/789_2.png) [@jb4x](https://discourse.processing.org/u/jb4x)\
**Post date:** [August 14, 2018, 8:54am UTC](https://discourse.processing.org/t/pattern-attractors/2535/15 "2018-08-14T08:54:20Z")

</div>

Hey @tony,

I played around with your sketch and I got some points of interest drawn on the curve. There are still some cases where Interesting features are left over but it could maybe be a nice start to efficiently compute the minimum distance.

I was thinking about a recursive method where you take a start point and a end point of the curve, split it in section, find the section where the minimum is and use the start point and end point of that section to apply the function again.

Anyway, here is the modified version of your code:

```auto
Bezier bez;
Point selectedPoint;

// --- Palette ---
color red = #d04648;
color dark = #140c1c;
color yellow = #dad45e;
color light_blue = #6dc2ca;
color peppermint = #deeed6;

void setup() {
  
	//fullScreen();
	size(600, 600);
  //rectMode(CENTER);
  smooth();
  
  
  bez = new Bezier(width/2, height/2);
}

void draw() {
  background(dark);
	stroke(peppermint);
	strokeWeight(10);
	rect(0,0,width,height);
	
  bez.draw();
}

//=========================================
// Event Call-backs
//-----------------

void mousePressed() {
  selectedPoint = getPointUnderMouse();
}

void mouseDragged() {
  if (selectedPoint != null) {
    selectedPoint.x = mouseX;
    selectedPoint.y = mouseY;
    
    bez.recalculateCenter();
  }
}

void mouseReleased() {
  selectedPoint = null;
	println('bezier(' + 
					bez.start.x + ", " + bez.start.y + ", " + 
					bez.ctrlStart.x + ", " + bez.ctrlStart.y + ", " + 
					bez.ctrlEnd.x + ", " + bez.ctrlEnd.y + ", " + 
					bez.end.x + ", " + bez.end.y + ")");
}

//=========================================
// Classes
//------------------

class Bezier {
  Point start;
  Point end;
  Point ctrlStart;
  Point ctrlEnd;
	Point p1;
	Point p2;
	Point p3;
	Point p4;
  
  Bezier(float x, float y) {
    float len = 100;
    p1 = new Point(0, 0, peppermint);
		p2 = new Point(0, 0, peppermint);
		p3 = new Point(0, 0, peppermint);
		p4 = new Point(0, 0, peppermint);
		p5 = new Point(0, 0, peppermint);
    start = new Point(x - len, y - len, light_blue);
    end = new Point(x + len, y - len, yellow);
    ctrlStart = new Point(x - len, y + len, light_blue);
    ctrlEnd = new Point(x + len, y + len, yellow);
		recalculateCenter();
  }
  
  void recalculateCenter() {
		float a;
		float b;
		float c;
		float delta;
		float sqrtDelta;
		float x;
		float y;
		float t1;
		float t2;
		
		p1.x = 10;
		p1.y = 10;
		p2.x = 10;
		p2.y = 10;
		p3.x = 10;
		p3.y = 10;
		p4.x = 10;
		p4.y = 10;
		
		// Calculation for x
		a = (-start.x) + 3 * ctrlStart.x - 3 * ctrlEnd.x + end.x;
		b = 2 * (start.x - 2 * ctrlStart.x + ctrlEnd.x);
		c = (-start.x) + ctrlStart.x;
		
		delta = b*b - 4 * a * c;
		
		t1 = -1;
		t2 = -1;
		if (delta >= 0) {
			sqrtDelta = sqrt(delta);
			t1 = ((-b) - sqrtDelta) / (2 * a);
			t2 = ((-b) + sqrtDelta) / (2 * a);
		}
		
		if (t1 >= 0 && t1 <= 1) {
			p1.x = bezierPoint(start.x, ctrlStart.x, ctrlEnd.x, end.x, t1);
			p1.y = bezierPoint(start.y, ctrlStart.y, ctrlEnd.y, end.y, t1);
		}
		if (t2 >= 0 && t2 <= 1) {
			p2.x = bezierPoint(start.x, ctrlStart.x, ctrlEnd.x, end.x, t2);
			p2.y = bezierPoint(start.y, ctrlStart.y, ctrlEnd.y, end.y, t2);
		}
		
		
		// Calculation for y
		a = (-start.y) + 3 * ctrlStart.y - 3 * ctrlEnd.y + end.y;
		b = 2 * (start.y - 2 * ctrlStart.y + ctrlEnd.y);
		c = (-start.y) + ctrlStart.y;
		
		delta = b*b - 4 * a * c;
		
		t1 = -1;
		t2 = -1;
		if (delta >= 0) {
			sqrtDelta = sqrt(delta);
			t1 = ((-b) - sqrtDelta) / (2 * a);
			t2 = ((-b) + sqrtDelta) / (2 * a);
		}
		
		if (t1 >= 0 && t1 <= 1) {
			p3.x = bezierPoint(start.x, ctrlStart.x, ctrlEnd.x, end.x, t1);
			p3.y = bezierPoint(start.y, ctrlStart.y, ctrlEnd.y, end.y, t1);
		}
		if (t2 >= 0 && t2 <= 1) {
			p4.x = bezierPoint(start.x, ctrlStart.x, ctrlEnd.x, end.x, t2);
			p4.y = bezierPoint(start.y, ctrlStart.y, ctrlEnd.y, end.y, t2);
		}
		
		
		//Get mid point
		p5.x = bezierPoint(start.x, ctrlStart.x, ctrlEnd.x, end.x, 0.5);
		p5.y = bezierPoint(start.y, ctrlStart.y, ctrlEnd.y, end.y, 0.5);
  }
  
  void draw() {
    stroke(peppermint);
		strokeWeight(1);
    line(start.x, start.y, ctrlStart.x, ctrlStart.y);
    line(ctrlEnd.x, ctrlEnd.y, end.x, end.y);
  
    stroke(red);
		strokeWeight(3);
    noFill();
    bezier(
      start.x, start.y,
      ctrlStart.x, ctrlStart.y, 
      ctrlEnd.x, ctrlEnd.y, 
      end.x, end.y
		);
		
		start.draw();
    ctrlStart.draw();
    ctrlEnd.draw();
    end.draw();
		p1.draw();
		p2.draw();
		p3.draw();
		p4.draw();
		p5.draw();
		noFill();
  }
  
  Point[] getPoints() {
    return new Point[] {
      start, ctrlStart, ctrlEnd, end};
  }
}

class Point {
  float x, y;
  float drawSize = 16;
  color drawColor = light_blue;

  Point(float a, float b, color c) {
    x = a;
    y = b;
		drawColor = c;
  }
  
  void draw() {
    noStroke();
    fill(this.drawColor);
		ellipse(x, y, drawSize, drawSize);
  }
    
  boolean hotSpotContains(float x, float y) {
    return dist(x, y, this.x, this.y) < drawSize;
  }
  
  void updatePoints (Point newPoint) {
    x = newPoint.x;
    y = newPoint.y;
  }
}

//=========================================
// Helper functions
//------------------

Point getPointUnderMouse() {
  Point[] points = bez.getPoints();
  for (int j = 0; j < points.length; j++) {
    if (points[j].hotSpotContains(mouseX, mouseY))
      return points[j];
  }
  return null;
}

Point averagePoint (Point[] points) {
  float xSum = 0;
  float ySum = 0;
  for (int i = 0; i < points.length; i++) {
    xSum += points[i].x;
    ySum += points[i].y;
  }
  return new Point(xSum/points.length, ySum/points.length, peppermint);
}

```

---

<div class="post-metadata">

**Author:** ![tony](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/tony/32/949_2.png) [@tony](https://discourse.processing.org/u/tony)\
**Post date:** [August 14, 2018, 4:07pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/16 "2018-08-14T16:07:05Z")

</div>

> [@jb4x](#):
>
> I was thinking about a recursive method where you take a start point and a end point of the curve, split it in section, find the section where the minimum is and use the start point and end point of that section to apply the function again.

I think that’s an interesting idea! It’s almost like a binary search, but with distance-from-mouse.

Your idea also kind of relates to how the [de Casteljau algorithm](https://en.m.wikipedia.org/wiki/De_Casteljau%27s_algorithm) draws a curve via recursion.

---

<div class="post-metadata">

**Author:** ![jb4x](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/jb4x/32/789_2.png) [@jb4x](https://discourse.processing.org/u/jb4x)\
**Post date:** [August 14, 2018, 4:28pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/17 "2018-08-14T16:28:40Z")

</div>

Well… I thought about it today and there are some extreme cases that are real trouble makers.

Take the image below:

 ![BezierExample](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/5/50c90562a3c850e3c0883cb13db21a7aec2ddc3b.jpeg)

If we try to figure out the distance from the yellow dot to the Bezier curve the subdivision is really important and starting with a too low subdivision might lead to the algorithm think the minimum is in the wrong area…

I tried to cut the shape in half and use a 3 degree bezier curve to get each half but sadly it does not fit. It would have been great since a 3 degree bezier curve is represented by a 2nd degree polynom so then, when trying to figure out the smallest distance, one would have taken the squared and tried to found the roots of the derivative which would have been of degree 3 (which we know the formulas).

Now, I think that with the interest points it is possible to approximate nicely the curve with just a few points and lines in between. Then we would just need to compute the distance from to point to each line a get the minimum.  
The question would be what is a good approximation depending on the user scenario…

---

<div class="post-metadata">

**Author:** ![WakeMeAtThree](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/wakemeatthree/32/38_2.png) [@WakeMeAtThree](https://discourse.processing.org/u/WakeMeAtThree)\
**Post date:** [August 15, 2018, 11:00am UTC](https://discourse.processing.org/t/pattern-attractors/2535/18 "2018-08-15T11:00:14Z")

</div>

Hello all!

Curve attractors used to be the `"Hello, world"` of grasshopper sketches, it’s interesting to see how simplified it was to do when it was embedded in Rhino versus making it from scratch here. I’ll be using processing-python mode to do this because it’s much simpler to explain.

After setting up your grid of rectangles and ellipses, you need a way of drawing a curve from a small set of points. The way of drawing it should include a way to access the interpolated points for later use. One way of doing that is, let’s say you start with a list of four points as follows:

```python
    n = 4
    curvePoints = [PVector(1.0*i*width/n,random(height)) for i in range(n+1)]

```

Then you can define a number for `resolution` where you find all interpolation points in between these points:

```python
    resolution = 50
    interpolations = [lerpVectors(curvePoints,
                                  1.0*i/(resolution-1)) for i in range(resolution)]

```

`lerpVectors()` is essentially a function that takes in a _list of points_ to interpolate in between and an interpolation _amount_ (shout to @jeremydouglass for showing me this technique [here](https://discourse.processing.org/t/processing-unfolding-columns/610/6?u=wakemeatthree))

```python
def lerpVectors(vecs, amt):
    if(len(vecs)==1): return vecs[0]
    
    spacing = 1.0/(len(vecs)-1);
    lhs = floor(amt / spacing);
    rhs = ceil(amt / spacing);
    
    try:
        return PVector.lerp(vecs[lhs], vecs[rhs], amt%spacing/spacing);
    except:
        return PVector.lerp(vecs[constrain(lhs, 0, len(vecs)-2)], vecs[constrain(rhs, 1, len(vecs)-1)], amt);

```

![linearoutput](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/a/af32b96ef744d20f82131f584ae1f8da82e37777.png)

This function as it is will return linear interpolations, hence just a linear output. There are other kinds of interpolations (check [this article out](http://paulbourke.net/miscellaneous/interpolation/)). I will use instead a custom cosine interpolation to get some simple curves, but you can play with that and read more from the article.

```python
def cosineLerpVec(v1,v2,amt):
    x = lerp(v1.x,v2.x,amt)
    y = cosineLerp(v1.y,v2.y,amt)
    return PVector(x,y)
def cosineLerp(y1,y2,mu):
    mu2 = (1-cos(mu*PI))/2
    return y1*(1-mu2)+y2*mu2

def lerpVectors(vecs, amt):
    if(len(vecs)==1): return vecs[0]
    
    spacing = 1.0/(len(vecs)-1);
    lhs = floor(amt / spacing);
    rhs = ceil(amt / spacing);
    
    try:
        return cosineLerpVec(vecs[lhs], vecs[rhs], amt%spacing/spacing);
    except:
        return cosineLerpVec(vecs[constrain(lhs, 0, len(vecs)-2)], vecs[constrain(rhs, 1, len(vecs)-1)], amt);

```

![cosineoutput](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/3/3cc0f9bf23f8d82d9b23f5c47ae86c1bc84472f6.png)

Now the attractor part becomes simple. What we need to do is measure distances of each point in the grid with a point on the curve. The point in the grid will take the minimum distance to the curve. (`ellipsePositions` is a list of X by Y grid points):

```python
    # Get distances
    distances = []
    for i in ellipsePositions:
        d = []
        
        # Get all distances to curve points
        for j in interpolations:
            d.append(PVector.dist(i,j))
        
        # Get minimum distance (closest point to curve)
        distances.append(min(d))
    
    # Normalize distances for later scaling
    distNormed = [ map(i,min(distances),max(distances),0.0,1.0)
                   for i in distances ]

```

The result can now be easily displayed if we call distances and ellipse positions together:

```python
    for i,j in zip(distNormed,ellipsePositions):
        diameter = i*spaceX
        with pushStyle():
            fill(0)
            ellipse(j.x,j.y,diameter,diameter)
        rect(j.x,j.y,spaceX,spaceX)

```

![finaloutput](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/9/97fb9741057ced97305f19dcf7f7d8b843eda63e.png)

Code in the end.

> **Summary**
>
> ```python
> def setup():
> size(400,400)
> background(255)
> noFill()
> rectMode(CENTER)
>     
> X = 25
> Y = 25
>     
> spaceX = 1.0*width/X
> spaceY = 1.0*height/Y
>     
> ellipsePositions = [PVector(i*spaceX,j*spaceY) for i in range(X+1) for j in range(Y+1)]
> n = 4
> curvePoints = [PVector(1.0*i*width/n,random(height)) for i in range(n+1)]
> resolution = 50
> interpolations = [lerpVectors(curvePoints,
> 1.0*i/(resolution-1)) for i in range(resolution)]
> # Get distances
> distances = []
> for i in ellipsePositions:
> d = []
>         
> # Get all distances to curve points
> for j in interpolations:
> d.append(PVector.dist(i,j))
>         
> # Get minimum distance (closest point to curve)
> distances.append(min(d))
>     
> # Normalize distances for later scaling
> distNormed = [ map(i,min(distances),max(distances),0.0,1.0)
> for i in distances ]
> # Display curve
> with beginShape():
> for i in interpolations:
> vertex(i.x,i.y)
>     
> # Display ellipses
> for i,j in zip(distNormed,ellipsePositions):
> diameter = i*spaceX
> with pushStyle():
> fill(0)
> ellipse(j.x,j.y,diameter,diameter)
> rect(j.x,j.y,spaceX,spaceX)
>     
> def cosineLerpVec(v1,v2,amt):
> x = lerp(v1.x,v2.x,amt)
> y = cosineLerp(v1.y,v2.y,amt)
> return PVector(x,y)
> def cosineLerp(y1,y2,mu):
> mu2 = (1-cos(mu*PI))/2
> return y1*(1-mu2)+y2*mu2
> 
> def lerpVectors(vecs, amt):
> if(len(vecs)==1): return vecs[0]
>     
> spacing = 1.0/(len(vecs)-1);
> lhs = floor(amt / spacing);
> rhs = ceil(amt / spacing);
>     
> try:
> return cosineLerpVec(vecs[lhs], vecs[rhs], amt%spacing/spacing);
> except:
> return cosineLerpVec(vecs[constrain(lhs, 0, len(vecs)-2)], vecs[constrain(rhs, 1, len(vecs)-1)], amt);
> 
> ```

I tried to break down thought process on this example as much as possible. Let me know if you need a clarification on anything that’s not clear. You could go off on a lot of tangents here: animate it, superimpose it, change display (doesn’t have to be ellipses diameters) etc. I’m interested to see what you do with this.

---

<div class="post-metadata">

**Author:** ![dan850](https://avatars.discourse-cdn.com/v4/letter/d/e9c0ed/32.png) [@dan850](https://discourse.processing.org/u/dan850)\
**Post date:** [August 16, 2018, 5:41am UTC](https://discourse.processing.org/t/pattern-attractors/2535/19 "2018-08-16T05:41:26Z")

</div>

> [@jb4x](#):
>
> PS: Is there a way to properly display math formulas?

sadly, there doesn’t seem to be a button for that format. But, there is a plugin out there that i will have the admins look at. - [Discourse Math - plugin - Discourse Meta](https://meta.discourse.org/t/discourse-math-plugin/65770)

---

<div class="post-metadata">

**Author:** ![jakejake](https://avatars.discourse-cdn.com/v4/letter/j/e495f1/32.png) [@jakejake](https://discourse.processing.org/u/jakejake)\
**Post date:** [August 16, 2018, 5:29pm UTC](https://discourse.processing.org/t/pattern-attractors/2535/20 "2018-08-16T17:29:30Z")

</div>

@WakeMeAtThree thats great! The explanation is really helpful. I can’t wait to play around with this.

I tried to convert your program to java but it’s not quite there, I don’t think the minimum distances are getting stored correctly. Any suggestions?

```auto
int X = 25;
int Y = 25;
int n = 4;

PVector[] ellipsePositions = new PVector[X*Y];
PVector[] curvePoints = new PVector[n];
PVector[] interpolations;

void setup(){
    size(400,400);
    background(255);
    noFill();
    rectMode(CENTER);
    
    float spaceX = 1.0*width/X;
    float spaceY = 1.0*height/Y;
    
    for(int i = 0; i < X; i++){
      for(int j = 0; j < Y; j++){
        ellipsePositions[(i*Y)+j] = new PVector(i*spaceX,j*spaceY);
      }
    }
    
    
    
    for(int p = 0; p < n; p++){
      curvePoints [p] = new PVector(1.0*p*width/float(n),random(height));
    }
    
    int resolution = 50;
    interpolations = new PVector[resolution];
    
    for(int r = 0; r < resolution; r++){
    interpolations[r] = lerpVectors(curvePoints, 1.0*r/(resolution));
    }
    
    // Get distances
    float[] distances = new float[ellipsePositions.length];
    
    for(int pt = 0; pt < ellipsePositions.length; pt++){
      float[] d = new float[interpolations.length];
      
      // Get all distances to curve points
      for(int j = 0; j < interpolations.length; j++){
          
          d[j] = PVector.dist(ellipsePositions[pt],interpolations[j]);
      }
      // Get minimum distance (closest point to curve)
      distances[pt] = min(d);
    }
    
    // Normalize distances for later scaling
    float[] distNormed = new float[distances.length];
    for(int dn = 0; dn < distances.length; dn++){
      distNormed[dn] = map(dn,min(distances),max(distances),0.0,1.0);
    }
    // Display curve
    beginShape();
    for (PVector i : interpolations){
        vertex(i.x,i.y);
    }
    endShape();
    
    // Display ellipses
    //for i,j in zip(distNormed,ellipsePositions):
    for(int j = 0; j < ellipsePositions.length; j++){
        float diameter = distNormed[j]*spaceX;
        fill(0);
        noStroke();
        ellipse(ellipsePositions[j].x, ellipsePositions[j].y,diameter,diameter);
        noFill();
        stroke(0);
        rect(ellipsePositions[j].x, ellipsePositions[j].y,spaceX,spaceX);
    }
}

PVector cosineLerpVec(PVector v1, PVector v2, float amt){
    float x = lerp(v1.x,v2.x,amt);
    float y = cosineLerp(v1.y,v2.y,amt);
    return new PVector(x,y);
}

float cosineLerp(float y1, float y2, float mu){
    float mu2 = (1-cos(mu*PI))/2;
    return y1*(1-mu2)+y2*mu2;
}

PVector lerpVectors(PVector[] vecs, float amt){ //amt is lerp position from resolution
    if(vecs.length==1){
      return vecs[0];
    }
    
    float spacing = 1.0/(vecs.length-1); //dist between two points
    int lhs = floor(amt / spacing); //find position start?
    int rhs = ceil(amt / spacing); // find position end?
    
    try{
        return cosineLerpVec(vecs[lhs], vecs[rhs], amt%spacing/spacing);
    }
    catch(Exception e){
        e.printStackTrace();
        return cosineLerpVec(vecs[constrain(lhs, 0, vecs.length-2)], vecs[constrain(rhs, 1, vecs.length-1)], amt);
    }
}

```

[Next page](https://discourse.processing.org/t/pattern-attractors/2535.md?page=2)
