# How to link grid to image as a height map

I’m interested in morphing the grid with randomness while also having an image interact with the grid. How do I go about designing varying cells and joining them together as a grid image? Can I use the random(); with (TRIANGLE_STRIP),(QUAD_STRIP) to create a variation?

An image of a grid with random variation.

My current code using (TRIANGLE_STRIP);.

Hi @asymmetric,

Here is an example how to link grid points to an image, and how you can move

• type r to switch random on and off
• type space to reset
• if random mode off you can capture the points and move them by mouse in a predefined range

You can play around with it a bit to get a better understanding… hope that helps…

Cheers
— mnse

PS: to make it easier I striped down to 2D. If you’ve understand this, it would be easy for you to add the z axis as heightmap …

``````//helper variables

float scl = 50.;
int rows, cols;
boolean doRandomMove = false;

boolean mode = true;
GridPoint[][] grid;
// image used for texture and heightmap
PImage img;

class GridPoint {
float ox, oy, ou, ov;
float x, y, u, v;
float sz=10;
boolean locked;
boolean dragged;
float angle;
public GridPoint(float px, float py, float pu, float pv, boolean plocked) {
ox=x=px;
oy=y=py;
ou=u=pu;  // u-pos of the image
ov=v=pv;  // v-pos of the image
locked = plocked;
dragged=false;
angle = random(TWO_PI);
}

public void reset() {
x=ox;
y=oy;
u=ou;
v=ov;
}

// automover
public void move() {
if (!locked) {
PVector r=PVector.random2D();
x=ox+0.2*scl*cos(angle);
y=oy+0.2*scl*sin(angle);
angle+=random(0.05, 0.1);
}
}

// manual mouse movement below
public void capture(float mx, float my) {
if (dist(mx, my, x, y) < sz) {
dragged=true;
} else
dragged=false;
}

public void move(float mx, float my) {
if (dragged) {
// remove check to not bound to surrounding box
if (dist(mx, my, ox, oy) < scl) {
x=mx;
y=my;
// uncomment move point but keep image
//u=x/((cols-1)*scl);
//v=y/((rows-1)*scl);
}
}
}

public void release() {
dragged=false;
}

public void render() {
pushStyle();
noStroke();
if (locked)
fill(255, 0, 0);
else if (dragged)
fill(255, 128, 64);
else
fill(0, 255, 0);

ellipse(x, y, sz, sz);
popStyle();
}
}

void setup() {
size(1000, 1000, P2D);
//  smooth(10);
// using normal to be independent to img size
textureMode(NORMAL);
// initialize our gridpoints
rows = floor(height/scl)+1;
cols = floor(width/scl)+1;
grid= new GridPoint[rows][cols];
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
float sx = x*scl;
float sy = y*scl;
// u,v can also be written by x/(cols-1), y/(rows-1) casted to floats
grid[y][x] = new GridPoint(sx, sy, sx/((cols-1)*scl), sy/((rows-1)*scl), (x==0|y==0|x==cols-1||y==rows-1));
}
}
}

void mousePressed() {
if (!doRandomMove) {
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
grid[y][x].capture(mouseX, mouseY);
}
}
}
}

void mouseDragged() {
if (!doRandomMove) {
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
grid[y][x].move(mouseX, mouseY);
}
}
}
}

void mouseReleased() {
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
grid[y][x].release();
}
}
}

void keyPressed() {
if (key == ' ') {
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
grid[y][x].reset();
}
}
} else if (key == 'r') {
doRandomMove=!doRandomMove;
}
}

void updateRandom() {
for (int y = 0; y < rows; y ++) {
for (int x = 0; x < cols; x++) {
grid[y][x].move();
}
}
}

void draw() {
background(0);
if (doRandomMove) {
updateRandom();
}

// draw grid ... set noStroke to remove lines
stroke(0, 255, 0);
// build the surface/terrain by triangle strips to display it
for (int y = 0; y < rows-1; y ++) {
texture(img); // on textured mode set the texture
// common triangle strips
for (int x = 0; x < cols; x++) {
GridPoint ca = grid[y][x];
vertex(ca.x, ca.y, ca.u, ca.v);
GridPoint cb = grid[y+1][x];
vertex(cb.x, cb.y, cb.u, cb.v);
}
endShape();
}

// draw grid points.
for (int y = 0; y < rows; y ++) {
for (int x = 0; x < cols; x++) {
grid[y][x].render();
}
}
}
``````
1 Like

Hi @asymmetric,

just for the sake of completness … I extended to 3D
Removed all the mouse picking stuff, as picking in 3D is another topic and would go beyond the scope here…

Cheers
— mnse

``````float scl = 8.;
int rows, cols;
int halfWidth;
int halfHeight;
boolean mode = true;

GridPoint[][] grid;
PImage img;

class GridPoint {
PVector position;
PVector oposition;
PVector uv;
boolean locked;
float phase;
public GridPoint(float x, float y, float z, float u, float v, boolean plocked) {
position  = new PVector(x, y, z);
oposition = position.copy();
uv = new PVector(u, v);
locked = plocked;
phase = 0;
}

public void reset() {
position = oposition.copy();
}

// automover
public void move() {
if (!locked) {
PVector wc = new PVector(position.x/25., position.y/25.);
position.z=25.*sin((sqrt(wc.x*wc.x + wc.y*wc.y)+phase));
phase-=0.1;
}
}

public void render() {
pushMatrix();
pushStyle();
noStroke();
if (locked)
fill(255, 0, 0);
else
fill(0, 255, 0);
translate(position.x, position.y, position.z);
box(3.);
popStyle();
popMatrix();
}
}

void setup() {
size(800, 800, P3D);
halfWidth=width/2;
halfHeight=height/2;
textureMode(NORMAL);

rows = floor(height/scl)+1;
cols = floor(width/scl)+1;
grid= new GridPoint[rows][cols];
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
float sx = x*scl;
float sy = y*scl;
grid[y][x] = new GridPoint(sx-halfWidth, sy-halfHeight, 0.0, sx/((cols-1)*scl), sy/((rows-1)*scl), false /*(x==0|y==0|x==cols-1||y==rows-1)*/);
}
}
}

void updateRandom() {
for (int y = 0; y < rows; y ++) {
for (int x = 0; x < cols; x++) {
grid[y][x].move();
}
}
}

void draw() {
background(0, 0, 64);
updateRandom();
directionalLight(255, 255, 255, 0, 0, -1);

translate(halfWidth, halfHeight, -400.);

noStroke();
if (mousePressed)
stroke(96);

for (int y = 0; y < rows-1; y ++) {
texture(img); // on textured mode set the texture
// common triangle strips
for (int x = 0; x < cols; x++) {
GridPoint ca = grid[y][x];
GridPoint cb = grid[y+1][x];
GridPoint cc = grid[y][x<cols-1 ? x+1 : x-1];
// this normal is good enough here
PVector n = PVector.sub(cb.position, ca.position).normalize().cross(PVector.sub(cc.position, ca.position).normalize());
normal(n.x, n.y, n.z);
vertex(ca.position.x, ca.position.y, ca.position.z, ca.uv.x, ca.uv.y);
normal(n.x, n.y, n.z);
vertex(cb.position.x, cb.position.y, cb.position.z, cb.uv.x, cb.uv.y);
}
endShape();
}
if (mousePressed) {
for (int y = 0; y < rows; y ++) {
for (int x = 0; x < cols; x++) {
grid[y][x].render();
}
}
}
}
``````
1 Like