处理:绘制矢量而不是像素

Processing: Draw vector instead of pixels

我有一个简单的Processing Sketch,绘制一条直径为20px的连续椭圆线。有没有办法修改草图,使其绘制矢量形状而不是像素?

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

} 

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

感谢所有能提供一些有用建议的人。

扩展我上面的评论,有几件事需要解决:

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

  2. draws vector shapes

目前您正在根据鼠标移动绘制椭圆。 一个副作用是,如果您将鼠标移动得足够快,椭圆之间就会有间隙。

为了填补空白,您可以算出每两个椭圆之间的距离。 如果距离大于这两个椭圆的大小,您可以在两者之间画一些。

PVector class 提供了一个 lerp() 函数,可让您轻松地在两点之间进行插值。 您可以阅读更多相关内容和 运行 一些示例 here

使用两点之间的这些距离与椭圆大小之间的比率来计算两者之间所需的点数。 下面是一个示例,它在您拖动鼠标时将鼠标位置存储到 PVectors 列表中:

//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();
}

注意点之间的插值是线性。 这是最简单的,但顾名思义,都是关于线条的: 它总是以直线连接两点,而不是曲线。

我添加了一个选项来控制插值椭圆打包在一起的紧密程度。下面是几个不同紧度级别的屏幕截图。您会注意到随着松紧度的增加,线条会变得更加明显:

你运行下面的代码:

//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 = [];
}

//
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.4/p5.min.js"></script>

如果您想要更平滑的线条,您将需要使用不同的插值法,例如二次插值法或三次插值法。您可以从现有的处理函数开始绘制曲线,例如 curve() or bezier(),and you'll find some helpful resources unrelated to Processing here,here and here.

矢量形状

您不是直接使用 pixels[],而是在绘制形状。 这些形状可以使用 Processing's PDF library 轻松保存为 PDF 查看 动画中的单帧(带屏幕显示) 示例。

这是按 's' 键时保存为 PDF 的版本:

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;
}

除了 George 的出色回答(我已经 +1)之外,我还想提供一个更基本的选项:

正如 George 所说,问题是当您移动鼠标时,您实际上跳过了一堆像素。因此,如果您只在 mouseX, mouseY 处绘制椭圆或点,那么最终会出现间隙。

愚蠢的修复: pmouseXpmouseY 变量保存光标的前一个位置。

这听起来可能不是很有用,但它们可以让您准确地解决您的问题。不是在当前鼠标位置绘制椭圆或点,而是从先前位置到当前位置绘制 直线。这将消除您的线条中的任何间隙。

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

无耻的自我推销:我已经写了一篇关于在可用处理中获取用户输入的教程 here

注意:只有当您每一帧都重新绘制背景时,这个愚蠢的解决方案才有效。如果您需要每帧重绘所有内容,那么乔治的答案就是您要走的路。