rotation - 使用处理“镜像”一个 PShape 对象(旋转/平移问题)
问题描述
我想“镜像”一个 PShape 对象,如下图所示:
我知道如何显示多个形状以及如何反转它们(下面的屏幕截图),但是当我必须旋转它们(并且可能翻译它们)时事情变得复杂,因为它们“坚持”到前面的形状(第一张图片)。
我一直在尝试计算原始形状(不规则四边形)的前 2 个顶点和函数的角度,atan2()
但无济于事。
如果有人可以帮助弄清楚如何解决这个问题,我将不胜感激。
int W = 20;
int H = 20;
int D = 20;
PShape object;
void setup(){
size(600, 600, P2D);
smooth();
}
void draw(){
background(255);
pushMatrix();
translate(width/2, height/1.3);
int td = -1;
for (int i = 0; i < 6; i++){
translate(0, td*H*2);
scale(-1, 1);
rotate(PI);
object();
td *= -1;
}
popMatrix();
}
void object() {
beginShape(QUADS);
vertex(-20, 20);
vertex(20, 0);
vertex(20, -20);
vertex(-20, -20);
endShape();
}
解决方案
要执行您想要的操作,您必须通过 2 个给定的角度为形状的顶部和底部angleT
以及“angleB”创建一个形状。原点 (0,0) 位于形状的中心。这导致旋转的枢轴位于形状斜率的中间:
int W = 40;
int H = 40;
float angleT = -PI/18;
float angleB = PI/15;
PShape object;
void object() {
float H1 = -H/2 + W*tan(angleB);
float H2 = H/2 + W*tan(angleT);
beginShape(QUADS);
vertex(-W/2, -H/2);
vertex(W/2, H1);
vertex(W/2, H2);
vertex(-W/2, H/2);
endShape();
}
绘制零件时,应区分偶数和奇数部分。必须通过反转 y 轴 ( ) 将部件水平翻转scale(1, -1)
。偶数部分必须由 的双倍旋转,angleB
奇数部分必须由 的双倍旋转angleT
。对于旋转,必须将斜坡的中心(枢轴)平移到原点:
void setup(){
size(600, 600, P2D);
smooth();
}
void draw(){
background(255);
translate(width/2, height/2);
float HC1 = -H/2 + W*tan(angleB)/2;
float HC2 = H/2 + W*tan(angleT)/2;
for (int i = 0; i < 15; i++){
float angle = (i % 2 == 0) ? -angleB : -angleT;
float HC = (i % 2 == 0) ? HC1 : HC2;
translate(0, -HC);
rotate(angle*2);
translate(0, -HC);
object();
scale(1, -1);
}
}
该算法适用于任何角度,包括 0 在内的正负角。
该算法可以进一步改进。假设您有一个由 4 个点 ( p0
, p1
, p2
, p3
) 定义的四边形:
float[] p0 = {10, 0};
float[] p1 = {40, 10};
float[] p2 = {60, 45};
float[] p3 = {0, 60};
PShape object;
void object() {
beginShape(QUADS);
vertex(p0[0], p0[1]);
vertex(p1[0], p1[1]);
vertex(p2[0], p2[1]);
vertex(p3[0], p3[1]);
endShape();
}
计算最小值、最大值、中心点、枢轴和角度:
float minX = min( min(p0[0], p1[0]), min(p2[0], p3[0]) );
float maxX = max( max(p0[0], p1[0]), max(p2[0], p3[0]) );
float minY = min( min(p0[1], p1[1]), min(p2[1], p3[1]) );
float maxY = max( max(p0[1], p1[1]), max(p2[1], p3[1]) );
float cptX = (minX+maxX)/2;
float cptY = (minY+maxY)/2;
float angleB = atan2(p1[1]-p0[1], p1[0]-p0[0]);
float angleT = atan2(p2[1]-p3[1], p2[0]-p3[0]);
float HC1 = p0[1] + (p1[1]-p0[1])*(cptX-p0[0])/(p1[0]-p0[0]);
float HC2 = p3[1] + (p2[1]-p3[1])*(cptX-p3[0])/(p2[0]-p3[0]);
像以前一样绘制形状:
for (int i = 0; i < 6; i++){
float angle = (i % 2 == 0) ? -angleB : -angleT;
float HC = (i % 2 == 0) ? HC1 : HC2;
translate(cptX, -HC);
rotate(angle*2);
translate(-cptX, -HC);
object();
scale(1, -1);
}
另一种方法是在两侧堆叠形状:
为此,您必须知道枢轴的高度 ( HC1
, HC2
) 和角度 ( angleB
, angleT
)。所以这可以基于上述两种方法来实现。
定义轴心点和上下边缘的方向:
PVector dir1 = new PVector(cos(angleB), sin(angleB));
PVector dir2 = new PVector(cos(angleT), sin(angleT));
PVector pv1 = new PVector(0, HC1); // or PVector(cptX, HC1)
PVector pv2 = new PVector(0, HC2); // or PVector(cptX, HC2)
计算两条边的交点 ( X
)。当然,这仅在
边缘不平行时才有效:
PVector v12 = pv2.copy().sub(pv1);
PVector nDir = new PVector(dir2.y, -dir2.x);
float d = v12.dot(nDir) / dir1.dot(nDir);
PVector X = pv1.copy().add( dir1.copy().mult(d) );
堆栈算法的工作原理如下:
for (int i = 0; i < 8; i++){
float fullAngle = angleT-angleB;
float angle = fullAngle * floor(i/2);
if ((i/2) % 2 != 0)
angle += fullAngle;
if (i % 2 != 0)
angle = -angle;
float flip = 1.0;
if (i % 2 != 0)
flip *= -1.0;
if ((i/2) % 2 != 0)
flip *= -1.0;
pushMatrix();
translate(X.x, X.y);
rotate(angle);
scale(1, flip);
rotate(-angleB);
translate(-X.x, -X.y);
object();
popMatrix();
}
推荐阅读
- java - 如何为类中的 ArrayList 赋值?
- php - laravel 中的动态类别级别
- android - 如何搜索从数据库中输入edittext值
- tmux - Byobu-tmux vi-copy 在系统升级后不起作用(KDE Neon)
- java - log4j 指向错误的反斜杠日志路径
- regex - 错误:Big Query REGEX_EXTRACT 无法解析正则表达式:无效的 perl 运算符:(?<
- java - 如何在我的 Eclipse IDE 中删除此错误标志?
- selenium - 使用普通浏览器配置文件作为硒配置文件
- laravel - 转储($请求);在此代码中不起作用。拉拉维尔 5.6
- python - 比较元组值