首页 > 解决方案 > 如何手动将矩阵堆栈应用于处理中的坐标

问题描述

在处理中,当您应用矩阵变换时,您可以在画布上绘图,而不必担心 xy 坐标的“真实”位置。

我认为按照相同的逻辑,我可以使用 ParentApplet.get(x, y, width, height) 复制画布的一部分,并且它会自动移动 x 和 y,但它不会,它使用坐标作为原始输入而不对其应用矩阵堆栈。

因此,我认为处理该问题的最简单方法是将矩阵堆栈手动应用于我的 x、y、宽度、高度值,并将结果用作 get() 的输入。但是我找不到这样的功能,是否存在?

编辑:根据要求,这是我的问题的一个例子

所以这里的目标是绘制一个简单的形状,复制并粘贴它。没有翻译,没有问题:

void settings(){
    size(500, 500);
}

void draw() {
  background(255);
  
  // Fancy rectangle for visibility
  fill(255, 0 ,0);
  rect(0, 0, 100, 100);
  fill(0, 255, 0);
  rect(20, 20, 60, 60);
  
  // copy rectangle and paste it elsewhere
  PImage img = get(0, 0, 101, 101);
  image(img, 200, 200);
}

在此处输入图像描述

现在,如果我在绘制形状之前应用了一个平移矩阵,我希望我可以使用相同的 get() 代码来复制完全相同的绘图:

void settings(){
    size(500, 500);
}

void draw() {
  background(255);
  pushMatrix();
  translate(10, 10);
  
  // Fancy rectangle for visibility
  fill(255, 0 ,0);
  rect(0, 0, 100, 100);
  fill(0, 255, 0);
  rect(20, 20, 60, 60);
  
  // copy rectangle and paste it elsewhere
  PImage img = get(0, 0, 101, 101);
  image(img, 200, 200);
  
  popMatrix();
}

但它不起作用, get(0, 0, ..) 不使用当前变换矩阵从原点 (10, 10) 复制像素:

在此处输入图像描述

标签: processing

解决方案


您能否提供更多详细信息。可以使用pushMatrix()/PopMatrix()操作坐标系,您可以更进一步并手动将矩阵和向量相乘。

令人困惑的部分是您正在调用get(x,y,width,height)但没有显示您如何呈现该PImage部分。很难猜出您提到的矩阵堆栈。你可以发布一个示例片段吗?

如果您在相同的 x,y 处渲染它,您调用get()它时应该使用相同的 x,y 偏移渲染:

size(640, 360);
noFill();
strokeWeight(9);

PImage placeholderForPGraphics = loadImage("https://processing.org/examples/moonwalk.jpg");
image(placeholderForPGraphics, 0, 0);

int x = 420;
int y = 120;
int w = 32;
int h = 48;
// visualise region of interest
rect(x, y, w, h);

// grab the section sub PImage
PImage section = placeholderForPGraphics.get(x, y, w, h);

//filter the section to make it really standout
section.filter(THRESHOLD);
// display section at same location
image(section, x, y);

渲染 PImage 小节

关于矩阵堆栈,如果您处于 2D 模式(否则为PMatrix3D) ,您可以调用getMatrix()它将返回PMatrix2D 。这是当前矩阵堆栈在您调用它的状态下的副本(任何先前的操作都将被“烘焙”到这个堆栈中)。

例如:

PMatrix m = g.getMatrix();
printArray(m.get(new float[]{}));

g.printMatrix()应该更容易打印到控制台,但getMatrix()如果您需要一个实例来操作,则需要调用)

g您的 PGraphics 实例在哪里。

然后,您可以随意操作它:

m.translate(10, 20);
m.rotate(radians(30));
m.scale(1.5);

完成后记得调用applyMatrix()它:

g.applyMatrix(m);

尽管可能很琐碎,但我希望上面示例的修改版本能说明这个想法:

size(640, 360);
noFill();
strokeWeight(9);
// get the current transformation matrix
PMatrix m = g.getMatrix();
// print to console
println("before");
g.printMatrix();
// modify it
m.translate(160, 90);
m.scale(0.5);
// apply it
g.applyMatrix(m);
// print applied matrix
println("after");
g.printMatrix();


PImage placeholderForPGraphics = loadImage("https://processing.org/examples/moonwalk.jpg");
image(placeholderForPGraphics, 0, 0);

int x = 420;
int y = 120;
int w = 32;
int h = 48;
// visualise region of interest
rect(x, y, w, h);

// grab the section sub PImage
PImage section = placeholderForPGraphics.get(x, y, w, h);

//filter the section to make it really standout
section.filter(THRESHOLD);
// display section at same location
image(section, x, y);

这是另一个PGraphics使用矩阵变换的基本示例:

void setup(){
  size(360, 360);
  
  // draw something manipulating the coordinate system
  PGraphics pg = createGraphics(360, 360);
  pg.beginDraw();
  pg.background(0);
  pg.noFill();
  pg.stroke(255, 128);
  pg.strokeWeight(4.5);
  pg.rectMode(CENTER);
  pg.translate(180,180);
  for(int i = 0 ; i < 72; i++){
    pg.rotate(radians(5));
    pg.scale(0.95);
    //pg.rect(0, 0, 320, 320, 32, 32, 32, 32);
    polygon(6, 180, pg);
  }
  pg.endDraw();
  
  // render PGraphics
  image(pg, 0, 0);
  
  }

同心六边形

这是矫枉过正:同样的效果可以画得更简单,但重点是调用get()和使用转换矩阵。这是一个修改后的迭代,显示了与 相同的原理get(x,y,w,h),然后image(section,x,y)

void setup(){
  size(360, 360);
  
  // draw something manipulating the coordinate system
  PGraphics pg = createGraphics(360, 360);
  pg.beginDraw();
  pg.background(0);
  pg.noFill();
  pg.stroke(255, 128);
  pg.strokeWeight(4.5);
  pg.rectMode(CENTER);
  pg.translate(180,180);
  for(int i = 0 ; i < 72; i++){
    pg.rotate(radians(5));
    pg.scale(0.95);
    //pg.rect(0, 0, 320, 320, 32, 32, 32, 32);
    polygon(6, 180, pg);
  }
  pg.endDraw();
  
  // render PGraphics
  image(pg, 0, 0);

 
  // take a section of PGraphics instance
  int w = 180;
  int h = 180;
  int x = (pg.width - w) / 2;
  int y = (pg.height - h) / 2;
  PImage section = pg.get(x, y, w, h);
  
  // filter section to emphasise
  section.filter(INVERT);
  
  // render section at sampled location
  image(section, x, y);
  popMatrix();
  
}

void polygon(int sides, float radius, PGraphics pg){
  float angleIncrement = TWO_PI / sides;
  pg.beginShape();
  for(int i = 0 ; i <= sides; i++){
    float angle = (angleIncrement * i) + HALF_PI;
    pg.vertex(cos(angle) * radius, sin(angle) * radius);
  }
  pg.endShape();
}

部分呈现倒置

这是在孤立坐标空间中重新应用最后一个变换矩阵的最终迭代(使用推/弹出矩阵调用):

void setup(){
  size(360, 360);
  
  // draw something manipulating the coordinate system
  PGraphics pg = createGraphics(360, 360);
  pg.beginDraw();
  pg.background(0);
  pg.noFill();
  pg.stroke(255, 128);
  pg.strokeWeight(4.5);
  pg.rectMode(CENTER);
  pg.translate(180,180);
  for(int i = 0 ; i < 72; i++){
    pg.rotate(radians(5));
    pg.scale(0.95);
    //pg.rect(0, 0, 320, 320, 32, 32, 32, 32);
    polygon(6, 180, pg);
  }
  pg.endDraw();
  
  // render PGraphics
  image(pg, 0, 0);
  
  // take a section of PGraphics instance
  int w = 180;
  int h = 180;
  int x = (pg.width - w) / 2;
  int y = (pg.height - h) / 2;
  PImage section = pg.get(x, y, w, h);
  
  // filter section to emphasise
  section.filter(INVERT);
  
  // print last state of the transformation matrix
  pg.printMatrix();
  
  // get the last matrix state
  PMatrix m = pg.getMatrix();
  // isolate coordinate space
  pushMatrix();
  //apply last PGraphics matrix
  applyMatrix(m);
  // render section at sampled location
  image(section, x, y);
  popMatrix();
  
  save("state3.png");
}

void polygon(int sides, float radius, PGraphics pg){
  float angleIncrement = TWO_PI / sides;
  pg.beginShape();
  for(int i = 0 ; i <= sides; i++){
    float angle = (angleIncrement * i) + HALF_PI;
    pg.vertex(cos(angle) * radius, sin(angle) * radius);
  }
  pg.endShape();
}

应用 pmatrix

这是一个极端的例子,因为0.95缩小了 72 次,因此渲染的图像非常小。还要注意旋转是递增的。

更新根据您的更新片段,似乎混乱是围绕pushMatrix()get()

在您的场景中,pushMatrix()/translate()将偏移局部坐标系:即绘制元素的位置。 get()全局调用并使用绝对坐标。

如果您只使用翻译,您可以简单地存储翻译坐标并重新使用它们从同一位置采样:

int sampleX = 10;
int sampleY = 10;

void settings(){
    size(500, 500);
}

void draw() {
  background(255);
  pushMatrix();
  translate(sampleX, sampleY);
  
  // Fancy rectangle for visibility
  fill(255, 0 ,0);
  rect(0, 0, 100, 100);
  fill(0, 255, 0);
  rect(20, 20, 60, 60);
  
  // copy rectangle and paste it elsewhere
  PImage img = get(sampleX, sampleY, 101, 101);
  image(img, 200, 200);
  
  popMatrix();
}

更新 这里有几个关于如何计算而不是硬编码翻译值的例子:

void settings(){
    size(500, 500);
}

void setup() {
  background(255);
  pushMatrix();
  translate(10, 10);
  // Fancy rectangle for visibility
  fill(255, 0 ,0);
  rect(0, 0, 100, 100);
  fill(0, 255, 0);
  rect(20, 20, 60, 60);
  // local to global coordinate conversion using PMatrix
  // g is the global PGraphics instance every PApplet (sketch) uses
  PMatrix m = g.getMatrix();
  printArray(m.get(null));
  // the point in local coordinate system
  PVector local = new PVector(0,0);
  // multiply local point by transformation matrix to get global point
  // we pass in null to get a new PVector instance: you can make this more efficient by allocating a single PVector ad re-using it instead of this basic demo
  PVector global = m.mult(local,null);
  // copy rectangle and paste it elsewhere
  println("local",local,"->global",global);
  PImage img = get((int)global.x, (int)global.y, 101, 101);
  image(img, 200, 200);
  
  popMatrix();
}

要根据变换矩阵计算向量的位置,只需将向量乘以该矩阵即可。非常粗略地说,push/pop 矩阵会发生什么(每个 push/pop 堆栈使用一个转换矩阵,然后在全局坐标系中一直相乘)。(请注意关于高效/预分配矩阵和向量的评论)。

这在代码方面会更加冗长,如果您使用大量嵌套转换,可能需要进行一些规划,但是您可以更好地控制选择使用哪些转换。

一个更简单的解决方案可能是切换到P3D允许您使用的 OpenGL 渲染器screenX()进行screenY()此转换。(也结帐modelX()/ modelY()

void settings(){
    size(500, 500, P3D);
}

void draw() {
  background(255);
  pushMatrix();
  translate(10, 10);
  // Fancy rectangle for visibility
  fill(255, 0 ,0);
  rect(0, 0, 100, 100);
  fill(0, 255, 0);
  rect(20, 20, 60, 60);
  // local to global coordinate conversion using modelX,modelY
  float x = screenX(0, 0, 0);
  float y = screenY(0, 0, 0);
  println(x,y);
  PImage img = get((int)x, (int)y, 101, 101);
  image(img, 200, 200);
  
  popMatrix();
}

请记住,您想要获取一个仅应用了平移的矩形。由于get()不会考虑旋转/缩放,因此对于更复杂的情况,您可能希望将局部坐标转换为全局坐标,不仅是左上角的坐标,还有带有偏移的右下角坐标。这个想法是围绕转换后的框计算更大的边界框(没有旋转),以便在调用时get()返回整个感兴趣区域(而不仅仅是裁剪部分)。


推荐阅读