问题描述
我想使用 Processing 重新创建抛物线三角形:
我认为我已经弄清楚了“基本”代码。 我现在需要的是循环我的代码以获得正确数量的三角形/线。
我正在使用 n
来确定应该绘制多少个三角形,正如您所看到的,当更改 n 时,线条位置是正确的。我只需要做一个循环,以便绘制正确数量的三角形。
这是我的代码:
float x1,y1,x2,y2,x3,y3;
float b,a;
float a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7,a8,b8,a9,b9;
void setup () {
size(600,600);
background(255);
x2=50;
y2=height-50;
x3=width-50;
y3=y2;
b=x3-x2;
a=b*sqrt(3)/2;
x1=x2+b/2;
y1=y2-a;
stroke(255,153);
line(x1,y2);
stroke(0,255,253);
line(x2,y3);
stroke(79,144,252);
line(x3,y3,x1,y1);
}
void draw() {
float n=4;
float div=(1/n);
a1=lerp(x1,div);
b1=lerp(y1,div);
a2=lerp(x2,div);
b2=lerp(y2,div);
a3=lerp(x3,div);
b3=lerp(y3,div);
a4=lerp(x1,div+div);
b4=lerp(y1,div+div);
a5=lerp(x2,div+div);
b5=lerp(y2,div+div);
a6=lerp(x3,div+div);
b6=lerp(y3,div+div);
a7=lerp(x1,div+div+div);
b7=lerp(y1,div+div+div);
a8=lerp(x2,div+div+div);
b8=lerp(y2,div+div+div);
a9=lerp(x3,div+div+div);
b9=lerp(y3,div+div+div);
line(a1,b2);
line(a2,b3);
line(a3,a1,b1);
line(a4,b5);
line(a5,b6);
line(a6,b4);
line(a7,b8);
line(a8,b9);
line(a9,b9,b7);
}
解决方法
正如之前在评论中提到的,有几点需要注意:
- 超过必要的插值
- 正确映射 lerp
t
参数以进行插值(代码中的div
)
关于插值,您的代码当前包含此部分:
a1=lerp(x1,x2,div);
b1=lerp(y1,y2,div);
a2=lerp(x2,x3,div);
b2=lerp(y2,y3,div);
a3=lerp(x3,x1,div);
b3=lerp(y3,y1,div);
a4=lerp(x1,div+div);
b4=lerp(y1,div+div);
a5=lerp(x2,div+div);
b5=lerp(y2,div+div);
a6=lerp(x3,div+div);
b6=lerp(y3,div+div);
a7=lerp(x1,div+div+div);
b7=lerp(y1,div+div+div);
a8=lerp(x2,div+div+div);
b8=lerp(y2,div+div+div);
a9=lerp(x3,div+div+div);
b9=lerp(y3,div+div+div);
line(a1,b1,a2,b2);
line(a2,b2,a3,b3);
line(a3,b3,a1,b1);
line(a4,b4,a5,b5);
line(a5,b5,a6,b6);
line(a6,b6,a4,b4);
line(a7,b7,a8,b8);
line(a8,b8,a9,b9);
line(a9,b9,a7,b7);
前 6 个插值(三角形的 3 个点之间)处理一个细分级别。使用 a1,c1,c2,c3
渲染线条应该可以解决问题。其余的插值似乎是在另一个细分级别手动插值。这是您希望通过 for
循环避免和改进的位复制/粘贴体力劳动。
关于插值参数,如果您知道细分的总数,您可以手动(使用除法)或使用 map()
函数将其映射到 t
参数
这是您的代码的修改版本,用于说明这些要点:
float x1,y3;
float b,a;
float a1,b3;
void setup () {
frameRate(10);
size(600,600);
background(255);
x2=50;
y2=height-50;
x3=width-50;
y3=y2;
b=x3-x2;
a=b*sqrt(3)/2;
x1=x2+b/2;
y1=y2-a;
}
void draw() {
background(255);
int subdivisions = frameCount % 300;
for(float n = 1; n < subdivisions; n++){
float div= n / (subdivisions - 1);
a1=lerp(x1,div);//x1x2
b1=lerp(y1,div);//y1y2
a2=lerp(x2,div);//x2x3
b2=lerp(y2,div);//y2y3
a3=lerp(x3,div);//x3x1
b3=lerp(y3,div);//y3y1
stroke(255,153);
line(a1,b2);//x1x2,y1y2,x2x3,y2y3
stroke(0,255,253);
line(a3,b1);//x3x1,y3y1,x1x2,y1y2
stroke(79,144,252);
line(a2,b3);//x2x3,y2y3,x3x1,y3y1
}
}
就我个人而言,我会将其全部封装在一个可重用的函数中(这将使其更容易在其他草图中重用)。下面是一个示例,其中我还将所有线条图一次性分组,与多个 line()
调用相反。这主要是一个偏好问题,应该会稍微加快速度,但是您永远不应该轻易牺牲可读性/灵活性。
float x1,a;
color c1 = color(255,153);
color c2 = color(0,253);
color c3 = color(79,252);
void setup () {
size(600,600);
background(255);
x2 = 50;
y2 = height - 50;
x3 = width - 50;
y3 = y2;
b = x3 - x2;
a = b * sqrt(3) / 2;
x1 = x2 + b / 2;
y1 = y2 - a;
}
void drawSubdivisions(int div,float x1,float y1,float x2,float y2,float x3,float y3,color c1,color c2,color c3){
// alternative rendering option: render the lines in a batch
beginShape(LINES);
// for each subdivision level
for(int i = 0; i < div; i++){
// map the subdivision level to the t interpolation parameter
float t = map(i,div - 1,0.0,1.0);
// interpolate each coordinate
// connecting the current triangle vertex to the next
float x1x2 = lerp(x1,t);
float y1y2 = lerp(y1,t);
float x2x3 = lerp(x2,t);
float y2y3 = lerp(y2,t);
// ... and last vertex to first vertex
float x3x1 = lerp(x3,t);
float y3y1 = lerp(y3,t);
// render lines for th interpolated shape
stroke(c1);
vertex(x1x2,y1y2);
vertex(x2x3,y2y3);
stroke(c2);
vertex(x2x3,y2y3);
vertex(x3x1,y3y1);
stroke(c3);
vertex(x3x1,y3y1);
vertex(x1x2,y1y2);
}
endShape();
}
void draw() {
// map the number of subdivisions
int subdivisions = round(map(mouseX,width,2,90));
background(0);
drawSubdivisions(subdivisions,c3);
text("move mouse on X axis to control subdivisions: " + subdivisions,10,15);
}
为了完整起见,这里有一个用 p5.js 编写的示例,您可以在下面运行。我将等边三角形概括为正多边形并使用颜色映射:
function setup() {
createCanvas(900,900);
colorMode(HSB,360,100,100);
}
function draw() {
background(0,100);
// map the number of points to Y axis
let numPoints = round(map(mouseY,height,3,6));
// map the number of subdivisions to the X axis
let subdivisions = round(map(mouseX,1,300));
// compute regular polygon points
let regularPolygonPoints = getRegularPolygonPoints(width * 0.5,height * 0.5,numPoints,300);
// optional: render regular polygon points
if(mouseIsPressed) drawPoints(regularPolygonPoints);
// compute and render interpolation lines between pairs of points
drawLinearInterpolation(regularPolygonPoints,subdivisions);
// instructions:
text(`mouseY -> numPoints:${numPoints}\nmouseX -> subdivisions: ${subdivisions}`,15);
}
// make a list of points (p5.Vector with x,y properties and conveniently lerp)
function getRegularPolygonPoints(x,y,sides,radius){
// output list
let points = [];
// calculate the angle per polygon side (same as 360 degrees / sides)
let angleIncrement = TWO_PI / sides;
// for each side
for(let i = 0 ; i < sides; i++){
// increment the angle (and offset by 90 degrees to point up)
let angle = (angleIncrement * i) - HALF_PI;
// insert the points into the list
// p5.Vector.fromAngle converts and angle radius to an x and y position (polar to cartesian)
// we offset(add) by x,y (transalation from 0,0 to center)
points.push(p5.Vector.fromAngle(angle,radius).add(x,y));
}
// return result
return points;
}
// draw lines between interpolated pairs of points in sequental side order
function drawLinearInterpolation(points,subdivisions){
// make strokes thinner the more subdivisions there are
strokeWeight((300 - subdivisions) / 600);
let numPoints = points.length;
beginShape(LINES);
// for each subdivision
for(let i = 0; i < subdivisions; i++){
let amount = map(i,subdivisions - 1,1.0);
// for each point index from first to last (and back to first)
for(let j = 0; j <= numPoints; j++){
// array access current,previous and next points
let current = points[j % numPoints];
let previous = points[(j-1) < 0 ? numPoints - 1 : j - 1];
let next = points[(j+1) % numPoints];
// interpolate between pairs of points: previous to current,current to next
let previousToCurrent = p5.Vector.lerp(previous,current,amount);
let currentToNext = p5.Vector.lerp(current,next,amount);
// play with stroke colour mapping
stroke((mouseIsPressed ? (i + j) : (i * j)) % 360,85,100);
// pass the point coordinates for shape rendering
vertex(previousToCurrent.x,previousToCurrent.y);
vertex(currentToNext.x,currentToNext.y);
}
}
endShape();
}
// render a list of points
function drawPoints(points){
strokeWeight(9);
stroke(0,0);
let numPoints = points.length;
beginShape(POINTS);
for(let i = 0 ; i < numPoints; i++){
let point = points[i];
vertex(point.x,point.y);
}
endShape();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
您可以选择使用不同的颜色映射。 lerpColor()
和 colorMode()
之类的函数可能很有用。玩得开心!