Convert 2D line drawing into 3D object in p5.js

Hi I’m very new to p5.js. I found p5 is very easy compare with threejs. I wanted to allow user to draw 2lines and convert in to 3D object then apply texture to the 3D.

I know the above is more for the forum question. I wants some guidence and also want to know whether it’s possible in p5.js.

Convert 2D drawing in to below image. Due to the new user restriction I couldn’t the line drawing but you can see it the below image.

Thanks in advance.Cheers!

Hi,

Welcome to the forum! :slight_smile:

One way to do this is to let the user click and draw points on the canvas in 2d. Each time you store the x and y coordinates and when the user has finished, you construct the 3d geometry with the beginShape() and endShape() functions.

Basically if you have 4 points in 2d space, you need to construct the 3d points to “solidify” your stroke like this :

Check out this tutorial for creating custom 3d meshes :

1 Like

Hi thanks for the guidance. I will try it and comment here. Thanks.

1 Like

Hi @josephh, I tried to draw line based on the mouse click. It’s not working as I expected.
I want similar to this demo https://jsfiddle.net/blackstrings/nh81deqw/

But above demo in three js and looks bit complicated. Can you please help me out?

Hi @sarath.jasrin,

Here is a small visualization in order to help you to better see how to solve this problem. You have a grid where you can draw points by clicking. When you are finished, you can press enter to toggle the 3d view where you can rotate with your mouse.

-->

// Whether the user finished drawing
boolean finishedDrawing = false;

// The path is a list of points
ArrayList<PVector> path;

void setup() {
  size(500, 500, P3D);
  
  // Initialize the path
  path = new ArrayList<PVector>();
}

void draw() {
  background(0);

  // If the user pressed enter
  if (finishedDrawing) {
    // Move the camera on the z axis (pointing toward the screen)
    camera(0, -100, width * 1.5, 0, 0, 0, 0, 1, 0);

    // Rotate on the y axis with the mouse 
    rotateY(map(mouseX, 0, width, 0, PI));
  } else {
    // Otherwise reset the camera
    // Those values were taken from https://processing.org/reference/camera_.html
    camera(0, 0, (height/2.0) / tan(PI*30.0 / 180.0), 0, 0, 0, 0, 1, 0);

    // Display text
    fill(255);
    textSize(18);
    text("Click to add points\nPress ENTER to see 3d", -width/2 + 50, -height/2 + 75);
  }
  
  drawAxis(100);
  
  pushMatrix();
  // translate to center the grid
  translate(-width / 2, -height / 2);
  
  // Draw the grid and the path
  drawGrid(50);
  drawPath();
  
  popMatrix();
}

void drawAxis(float unit) {
  strokeWeight(5);
  textSize(40);

  // x axis
  stroke(255, 0, 0);
  line(0, 0, 0, unit, 0, 0);
  fill(255, 0, 0);
  text("X", unit + 5, 0, 0);
  
  // y axis
  stroke(0, 255, 0);
  line(0, 0, 0, 0, unit, 0);
  fill(0, 255, 0);
  text("Y", 0, unit + 5, 0);
  
  if (finishedDrawing) {
    // z axis
    stroke(0, 0, 255);
    line(0, 0, 0, 0, 0, unit);
    fill(0, 0, 255);
    text("Z", 0, 0, unit + 5);
  }
}

// Add a point when clicked (note that we didn't specify a Z coordinate)
void mousePressed() {
  if (!finishedDrawing) {
    path.add(new PVector(mouseX, mouseY));
  }
}

// Toggle the 3d view
void keyPressed() {
  if (keyCode == ENTER) {
    finishedDrawing = !finishedDrawing;
  }
}

// Draw the grid
void drawGrid(float cellSize) {
  stroke(255, finishedDrawing? 50 : 255);
  strokeWeight(1);

  //pushMatrix()
  for (int x = 0; x <= width; x += cellSize) {
    line(x, 0, x, height);
  }

  for (int y = 0; y <= height; y += cellSize) {
    line(0, y, width, y);
  }
}

void drawPath() {
  stroke(255, 0, 0);

  // Draw lines
  strokeWeight(3);
  for (int i = 0; i < path.size() - 1; i++) {
    line(path.get(i).x, path.get(i).y, path.get(i+1).x, path.get(i+1).y);
  }

  // Draw points
  strokeWeight(12);
  for (PVector point : path) {
    point(point.x, point.y);
  }
}

Again a great tutorial by Daniel Schiffman about P3D (OpenGL in processing) :

And also useful documentation ressources :

EDIT : sorry I just realized that you were asking for p5js but it’s the same concepts and you can easily transpose the code

1 Like

@josephh Thanks for the reply. Let me try!

@josephh I tried to convert into p5js it doesn’t seem like working…

// Whether the user finished drawing
let finishedDrawing = false;

// The path is a list of points
let path;

function setup() {
  createCanvas(400, 400,WEBGL);
  
  // Initialize the path
  path = [];
}

function draw() {
  background(0);

  // If the user pressed enter
  if (finishedDrawing) {
    // Move the camera on the z axis (pointing toward the screen)
    camera(0, -100, width * 1.5, 0, 0, 0, 0, 1, 0);

    // Rotate on the y axis with the mouse 
    rotateY(map(mouseX, 0, width, 0, PI));
  } else {
    // Otherwise reset the camera
    // Those values were taken from https://processing.org/reference/camera_.html
    camera(0, 0, (height/2.0) / tan(PI*30.0 / 180.0), 0, 0, 0, 0, 1, 0);

    // Display text
    fill(255);
    textSize(18);
    textFont('Georgia');
    text("Click to add points\nPress ENTER to see 3d", -width/2 + 50, -height/2 + 75);
  }
  
  drawAxis(100);
  
  push();
  // translate to center the grid
  translate(-width / 2, -height / 2);
  
  // Draw the grid and the path
  drawGrid(50);
  drawPath();
  
  pop();
}

function drawAxis(unit) {
  strokeWeight(5);
  textSize(40);
textFont('Georgia');
  // x axis
  stroke(255, 0, 0);
  line(0, 0, 0, unit, 0, 0);
  fill(255, 0, 0);
  text("X", unit + 5, 0, 0);
  
  // y axis
  stroke(0, 255, 0);
  line(0, 0, 0, 0, unit, 0);
  fill(0, 255, 0);
  textFont('Georgia');
  text("Y", 0, unit + 5, 0);
  
  if (finishedDrawing) {
    // z axis
    stroke(0, 0, 255);
    line(0, 0, 0, 0, 0, unit);
    fill(0, 0, 255);
    textFont('Georgia');
    text("Z", 0, 0, unit + 5);
  }
}

// Add a point when clicked (note that we didn't specify a Z coordinate)
function mousePressed() {
  if (!finishedDrawing) {
    path.add(new PVector(mouseX, mouseY));
  }
}

// Toggle the 3d view
function keyPressed() {
  if (keyCode == ENTER) {
    finishedDrawing = !finishedDrawing;
  }
}

// Draw the grid
function drawGrid(cellSize) {
  stroke(255, finishedDrawing? 50 : 255);
  strokeWeight(1);

  //pushMatrix()
  for (let x = 0; x <= width; x += cellSize) {
    line(x, 0, x, height);
  }

  for (let y = 0; y <= height; y += cellSize) {
    line(0, y, width, y);
  }
}

function drawPath() {
  stroke(255, 0, 0);

  // Draw lines
  strokeWeight(3);
  for (let i = 0; i < path.length - 1; i++) {
    line(path.x, path.y, path[i+1].x, path[i+1].y);
  }

  // Draw points
  strokeWeight(12);
  path.forEach(p=>{
    point(p.x, p.y);
  })
 
}

Ok so the process of writing code is often linked to debugging and solving errors.

In this case, the console is going to be our friend (access it with F12 on most browsers or use the p5js web editor).

The first message is this :

WEBGL: you must load and set a font before drawing text. See loadFont and textFont for more details.

It’s because in WEBGL, you must load a font yourself as mentioned here :

WEBGL : Only fonts loaded via loadFont() are supported.

Next when you click, you have an error :

ReferenceError: PVector is not defined (sketch: line 77)

This is because PVector is a class defined in Processing Java not in p5js in JavaScript. Instead use :

3 Likes