Processing: Draw vector instead of pixels

笑着哭i 提交于 2019-12-12 04:24:40

问题


I have a simple Processing Sketch, drawing a continuous line of ellipses with a 20px diameter. Is there a way to modify the sketch so that it draws vector shapes instead of pixels?

  void setup() {
  size(900, 900); 
  background(110, 255, 94);  

} 

void draw() {
  ellipse(mouseX, mouseY, 20, 20);
 fill(255);
}

Thanks to everyone who can provide some helpful advice.


回答1:


Expanding my comment above, there a couple of things to tackle:

  1. drawing a continuous line of ellipses with a 20px diameter

  2. draws vector shapes

Currently you're drawing ellipses based on mouse movement. A side effect is that if you move the mouse fast enough you will have gaps in between ellipses.

To fill the gaps you can work out the distance between every two ellipses. If the distance is greater than the sizes of these two ellipses you can draw some in between.

The PVector class provides a lerp() function that allows you easily interpolate between two points. You can read more on this and run some examples here

Using the ratio between these distance of two points and the ellipse size the number of points needed in between. Here is an example that stores mouse locations to a list of PVectors as you drag the mouse:

//create an array list to store points to draw
ArrayList<PVector> path = new ArrayList<PVector>();
//size of each ellipse
float size = 20;
//how tight will the extra ellipses be drawn together
float tightness = 1.25;

void setup() {
  size(900, 900);
} 

void draw() {
  background(110, 255, 94);
  fill(255);

  //for each point in the path, starting at 1 (not 0)
  for(int i = 1; i < path.size(); i++){

    //get a reference to the current and previous point
    PVector current  = path.get(i);
    PVector previous = path.get(i-1);

    //calculate the distance between them
    float distance = previous.dist(current);

    //work out how many points will need to be added in between the current and previous points to keep the path continuous (taking the ellipse size into account) 
    int extraPoints = (int)(round(distance/size * tightness));

    //draw the previous point
    ellipse(previous.x,previous.y,size,size);

    //if there are any exta points to be added, compute and draw them:
    for(int j = 0; j < extraPoints; j++){

      //work out a normalized (between 0.0 and 1.0) value of where each extra point should be
      //think of this as a percentage along a line: 0.0 = start of line, 0.5 = 50% along the line, 1.0 = end of the line
      float interpolation = map(j,0,extraPoints,0.0,1.0);

      //compute the point in between using PVector's linear interpolation (lerp()) functionality
      PVector inbetween = PVector.lerp(previous,current,interpolation);

      //draw the point in between
      ellipse(inbetween.x,inbetween.y,size,size);
    }

  }

  //draw instructions
  fill(0);
  text("SPACE = clear\nLEFT = decrease tightness\nRIGHT = increase tightness\ntightness:"+tightness,10,15);
}

void mouseDragged(){
  path.add(new PVector(mouseX,mouseY));
}
void keyPressed(){
  if(keyCode == LEFT)  tightness = constrain(tightness-0.1,0.0,3.0);
  if(keyCode == RIGHT) tightness = constrain(tightness+0.1,0.0,3.0);
  if(key == ' ') path.clear();
}

Note that the interpolation between points is linear. It's the simplest, but as the name implies, it's all about lines: it always connects two points in a straight line, not curves.

I've added the option to control how tight interpolated ellipses will be packed together. Here are a couple of screenshots with different tightness levels. You'll notice as tightness increases, the lines will become more evident:

You run the code bellow:

//create an array list to store points to draw
var path = [];
//size of each ellipse
var ellipseSize = 20;
//how tight will the extra ellipses be drawn together
var tightness = 1.25;

function setup() {
  createCanvas(900, 900);
} 

function draw() {
  background(110, 255, 94);
  fill(255);
  
  //for each point in the path, starting at 1 (not 0)
  for(var i = 1; i < path.length; i++){
    
    //get a reference to the current and previous point
    var current  = path[i];
    var previous = path[i-1];
    
    //calculate the distance between them
    var distance = previous.dist(current);
    
    //work out how many points will need to be added in between the current and previous points to keep the path continuous (taking the ellipse size into account) 
    var extraPoints = round(distance/ellipseSize * tightness);
    
    //draw the previous point
    ellipse(previous.x,previous.y,ellipseSize,ellipseSize);
    
    //if there are any exta points to be added, compute and draw them:
    for(var j = 0; j < extraPoints; j++){
      
      //work out a normalized (between 0.0 and 1.0) value of where each extra point should be
      //think of this as a percentage along a line: 0.0 = start of line, 0.5 = 50% along the line, 1.0 = end of the line
      var interpolation = map(j,0,extraPoints,0.0,1.0);
      
      //compute the point in between using PVector's linear interpolation (lerp()) functionality
      var inbetween = p5.Vector.lerp(previous,current,interpolation);
      
      //draw the point in between
      ellipse(inbetween.x,inbetween.y,ellipseSize,ellipseSize);
    }
    
  }
  
  //draw instructions
  fill(0);
  text("BACKSPACE = clear\n- = decrease tightness\n+ = increase tightness\ntightness:"+tightness,10,15);
}

function mouseDragged(){
  path.push(createVector(mouseX,mouseY));
}
function keyPressed(){
  if(keyCode == 189)  tightness = constrain(tightness-0.1,0.0,3.0);
  if(keyCode == 187) tightness = constrain(tightness+0.1,0.0,3.0);
  if(keyCode == BACKSPACE) path = [];
}

//https://stackoverflow.com/questions/40673192/processing-draw-vector-instead-of-pixels
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.4/p5.min.js"></script>

If you want smoother lines you will need to use a different interpolation such as quadratic or cubic interpolation. You can start with existing Processing functions for drawing curves such as curve() or bezier(),and you'll find some helpful resources unrelated to Processing here,here and here.

On vector shapes

You're not directly working with pixels[], you're drawing shapes. These shapes can easily be saved to PDF using Processing's PDF library Check out the Single Frame from an Animation (With Screen Display) example.

Here is a version that saves to PDF when pressing the 's' key:

import processing.pdf.*;

//create an array list to store points to draw
ArrayList<PVector> path = new ArrayList<PVector>();
//size of each ellipse
float size = 20;
//how tight will the extra ellipses be drawn together
float tightness = 1.25;

//PDF saving
boolean record;

void setup() {
  size(900, 900);

} 

void draw() {
  background(110, 255, 94);
  fill(255);

  //if we need to save the current frame to pdf, begin recording drawing instructions
  if (record) {
    // Note that #### will be replaced with the frame number. Fancy!
    beginRecord(PDF, "frame-####.pdf"); 
  }

  //for each point in the path, starting at 1 (not 0)
  for(int i = 1; i < path.size(); i++){

    //get a reference to the current and previous point
    PVector current  = path.get(i);
    PVector previous = path.get(i-1);

    //calculate the distance between them
    float distance = previous.dist(current);

    //work out how many points will need to be added in between the current and previous points to keep the path continuous (taking the ellipse size into account) 
    int extraPoints = (int)(round(distance/size * tightness));

    //draw the previous point
    ellipse(previous.x,previous.y,size,size);

    //if there are any exta points to be added, compute and draw them:
    for(int j = 0; j < extraPoints; j++){

      //work out a normalized (between 0.0 and 1.0) value of where each extra point should be
      //think of this as a percentage along a line: 0.0 = start of line, 0.5 = 50% along the line, 1.0 = end of the line
      float interpolation = map(j,0,extraPoints,0.0,1.0);

      //compute the point in between using PVector's linear interpolation (lerp()) functionality
      PVector inbetween = PVector.lerp(previous,current,interpolation);

      //draw the point in between
      ellipse(inbetween.x,inbetween.y,size,size);
    }

  }
  //once what we want to save has been recorded to PDF, stop recording (this will skip saving the instructions text);
  if (record) {
    endRecord();
    record = false;
    println("pdf saved");
  }

  //draw instructions
  fill(0);
  text("SPACE = clear\nLEFT = decrease tightness\nRIGHT = increase tightness\ntightness:"+tightness+"\n's' = save PDF",10,15);
}

void mouseDragged(){
  path.add(new PVector(mouseX,mouseY));
}
void keyPressed(){
  if(keyCode == LEFT)  tightness = constrain(tightness-0.1,0.0,3.0);
  if(keyCode == RIGHT) tightness = constrain(tightness+0.1,0.0,3.0);
  if(key == ' ') path.clear();
  if(key == 's') record = true;
}



回答2:


In addition to George's great answer (which I've +1'd), I wanted to offer a more basic option:

The problem, like George said, is that when you move the mouse, you actually skip over a bunch of pixels. So if you only draw ellipses or points at mouseX, mouseY then you'll end up with gaps.

The dumb fix: the pmouseX and pmouseY variables hold the previous position of the cursor.

That might not sound very useful, but they allow you to solve exactly your problem. Instead of drawing ellipses or points at the current mouse position, draw a line from the previous position to the current position. This will eliminate any gaps in your lines.

void draw(){
 line(pmouseX, pmouseY, mouseX, mouseY); 
}

Shameless self-promotion: I've written a tutorial on getting user input in Processing available here.

Note: This dumb solution will only work if you aren't redrawing the background every frame. If you need to redraw everything every frame, then George's answer is the way to go.



来源:https://stackoverflow.com/questions/40673192/processing-draw-vector-instead-of-pixels

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!