I have this code and I should modify it to do 2 things:
draw the surface in solid mode using the basic lighting (lights() and a unique fill()) and
draw the surface with a two color gradient (red for low values of z and yellow for high values, for instance) For that,I was advised use a fill() call before each vertex()
This is my code for the 1st one, the problem is that I do not want the grid to show after I have applied the color.
// Drawing a 3D function
float rotX = 0.0, rotY = 0.0;
int lastX, lastY;
float distX = 0.0, distY = 0.0;
// Function steps
int steps = 50;
// z scale
float scaleZ = 200.0;
// z zoom
float zoomZ = -300.0;
// Graphic size
float gX = 500.0, gY = 500.0;
void setup()
{
size(500, 500, P3D);
noFill();
strokeWeight(0.005);
}
float function(float x, float y)
{
return x*x*x + y*y*y;
}
void draw() {
lights();
background(0);
// We center the results on window
translate(gX/2, gY/2, zoomZ);
// Rotation
rotateY(rotY + distX);
rotateX(rotX + distY);
// Centering around (0, 0);
translate(-gX/2, -gY/2);
// Function covers
// 400 x 400 x scaleZ
scale(gX, gY, scaleZ);
// Drawing the function
fill(167);
drawFunction();
// Drawing axes
stroke(255, 0, 0);
line(0,0,0,2000,0,0);
stroke(0,255,0);
line(0,0,0,0,2000,0);
stroke(0,0,255);
line(0,0,0,0,0,2000);
}
void drawFunction()
{
float x, y, z;
int i = 0, j = 0;
float in_steps = 1.0 / steps;
float[][] matrix = new float[steps+1][steps+1];
for (y = 0.0, j = 0; y <= 1.0; y+=in_steps, j++)
for (x = 0.0, i = 0; x <= 1.0; x+=in_steps, i++)
matrix[i][j] = function(x, y);
stroke(167);
for (j = 0, y = 0.0; j < steps; j++, y+=in_steps) {
beginShape(QUAD_STRIP);
for (i = 0, x = 0.0; i <= steps; i++, x+=in_steps) {
vertex(x, y, matrix[i][j]);
vertex(x, y + in_steps, matrix[i][j+1]);
}
endShape();
}
}
void mousePressed()
{
lastX = mouseX;
lastY = mouseY;
}
void mouseDragged()
{
distX = radians(mouseX - lastX);
distY = radians(lastY - mouseY);
}
void mouseReleased()
{
rotX += distY;
rotY += distX;
distX = distY = 0.0;
}
The lights() are already proper set in your code.
Use noStroke() to get rid of the lines. noStroke disables drawing outlines.
The color of the filled areas can be set by passing RGB (red, green and blue) values to fill().
The values are integral values in the range[0, 255]. The RGB vlaue for red is (255, 0, 0) and the RGB value for yellow is (255, 255, 0).
A gradient color form red to yellow can be achieved by
fill(255, z*255, 0);
where z is in [0.0, 1.0]. If z = 0.0 the result is red (255, 0, 0) and if z = 1.0 the result is yellow (255, 255, 0). All values for z in between 0.0 and 1.0 result in a linear interpolation between read and yellow.
e.g.
for (j = 0, y = 0.0; j < steps; j++, y+=in_steps) {
beginShape(QUAD_STRIP);
noStroke(); // no lines
for (i = 0, x = 0.0; i <= steps; i++, x+=in_steps) {
fill(255, matrix[i][j] * 255, 0); // interpolate between red and yellow
vertex(x, y, matrix[i][j]);
fill(255, matrix[i][j+1] * 255, 0); // interpolate between red and yellow
vertex(x, y + in_steps, matrix[i][j+1]);
}
endShape();
}
Related
I am trying to animate a flying cloth, which consists of individual rectangles, in Processing. I built the cloth from a nested loop. Now I want to use the noise function to manipulate the height of the individual points. Unfortunately, I can't do that properly - apparently I don't understand the function. I took a detour, drew noise clouds on a PGraphics, then read the brightness value - and use that to control the z-position of each rectangle. This works the way I want it to work!
Only - how can I achieve the same effect without going the detour via PGraphics?
This is my code the way I want it (the detour):
PGraphics pg;
float increment = 0.002;
float xoff;
float yoff;
float zoff = 0.0;
float zincrement = 0.1;
float size;
float pixel = 200;
void setup() {
size(1920, 1080, P3D);
frameRate(30);
size = width/pixel;
rectMode(CENTER);
pg = createGraphics(width, width);
}
void draw() {
//PGraphics
pg.beginDraw();
pg.loadPixels();
xoff = 0.0;
for (int x = 0; x < width; x++) {
xoff += increment;
yoff = 0.0;
for (int y = 0; y < width; y++) {
yoff += increment;
float bright = noise(xoff, yoff, zoff)*255;
pg.pixels[x+y*width] = color(bright, bright, bright);
}
}
pg.updatePixels();
pg.endDraw();
zoff += zincrement;
//SCENE
background(0);
stroke(255,0,0);
line(width/2, 0, width/2, height);
translate(0, 0, mouseX);
pushMatrix();
rotateX(radians(45));
translate(pixel*size/2, -pixel*size, -pixel*size);
rotateZ(radians(mouseX));
translate(-pixel*size/2, -pixel*size/2, -pixel*size/4);
pushMatrix();
for (int y = 0; y < pixel; y++) {
for (int x = 0; x < pixel; x++) {
color c = pg.get(int(size*x), int(size*y));
float zpoint = map(brightness(c), 0, 255, -500, 500);
pushMatrix();
noiseDetail(3, 0.5f);
translate(x*size, y*size, zpoint);
rotate(radians(45));
noStroke();
fill(255);
rect(0, 0, size, size);
popMatrix();
}
}
popMatrix();
popMatrix();
}
And this is how I would like to write it in a simpler way:
float size;
float pixel = 200;
float xoff;
float yoff;
float zoff;
float increment = 0.01;
void setup() {
size(1920, 1080, P3D);
frameRate(30);
size = width/pixel;
rectMode(CENTER);
}
void draw() {
background(0);
stroke(255,0,0);
line(width/2, 0, width/2, height);
translate(0,0,mouseX);
pushMatrix();
rotateX(radians(45));
translate(pixel*size/2,-pixel*size,-pixel*size);
rotateZ(radians(mouseX));
translate(-pixel*size/2,-pixel*size/2,-pixel*size/4);
pushMatrix();
for (int y = 0; y < pixel; y++) {
for (int x = 0; x < pixel; x++) {
pushMatrix();
noiseDetail(1, 0.25f);
float n = noise(x*size + xoff, y*size + yoff, zoff)*255;
translate(x*size, y*size, n);
rotate(radians(45));
noStroke();
fill(255);
rect(0, 0, size, size);
popMatrix();
}
}
popMatrix();
popMatrix();
yoff += 0.11;
xoff += 0.02;
zoff += 0.03;
}
I think this line is the problem - because currently it doesn't create a real cloth like this.
float n = noise(x*size + xoff, y*size + yoff, zoff)*255;
You are correct, the problem is in this line
float n = noise(x*size + xoff, y*size + yoff, zoff)*255;
The changes in the x and y input parameters from one iteration to the other is too large (it is equal to size). If you read about the Perlin noise you will know that with a large step it will just look like random noise. My suggestion to edit this line to something like:
float n = noise(xx + xoff, yy + yoff, zoff)*255;
where xx and yy are other variables that you increment slowly in the for loop
My solution:
void draw() {
background(0);
stroke(255,0,0);
line(width/2, 0, width/2, height);
translate(0,0,mouseX);
pushMatrix();
rotateX(radians(45));
translate(pixel*size/2,-pixel*size,-pixel*size);
rotateZ(radians(mouseX));
translate(-pixel*size/2,-pixel*size/2,-pixel*size/4);
pushMatrix();
float xx = 0;
float yy = 0;
for (int y = 0; y < pixel; y++) {
xx = 0;
for (int x = 0; x < pixel; x++) {
pushMatrix();
noiseDetail(1, 0.25f);
float n = noise(xx + xoff, yy + yoff)*255;
translate(x*size, y*size, n);
rotate(radians(45));
noStroke();
fill(255);
rect(0, 0, size, size);
popMatrix();
xx += 0.1;
}
yy += 0.1;
}
popMatrix();
popMatrix();
yoff += 0.11;
xoff += 0.02;
zoff += 0.03;
}
Changing the increment of xx and yy can make the flag look like its flowing in 1 direction...
I am trying to draw a grid over an image using the underlaying colours of the image to fill the circles. But some pixels are not getting the correct colour.
In this case the circles are drawn white but they should not be drawn white...
See my code below:
import processing.pdf.*;
PImage img;
color background = color(255);
void setup() {
size(1038, 525);
ellipseMode(CORNER);
noStroke();
//img = loadImage("noise2.jpg");
//img = loadImage("air.png");
img = loadImage("accidents.png");
image(img, 0, 0, width, height);
visualGrid(20, 0.4, false);
}
//void draw() {
// fill(noise.get(mouseX, mouseY));
// rect(width - 100, height - 100, 100, 100);
//}
void visualGrid(int circleSize, float fillSmoothing, boolean debug) {
float halfCircle = circleSize / 2.0;
int amountX = floor(width / circleSize);
int amountY = floor(height / circleSize);
amountY += floor(amountY * 0.1);
float offsetX = (width - (amountX * circleSize + halfCircle)) / 2 + halfCircle;
float offsetY = (height - amountY * circleSize + amountY * circleSize * 0.1) / 2;
for (int x = 0; x < amountX; x++) {
for (int y = 0; y < amountY; y++) {
float styledOffsetX = (y % 2 == 0) ? offsetX - halfCircle : offsetX;
float xpos = x * circleSize + styledOffsetX;
float ypos = circleSize * 0.9 * y + offsetY;
int sectionSize = round(circleSize * fillSmoothing);
float sectionOffset = (circleSize - sectionSize) / 2;
color c = getAvgImgColor(img.get(round(xpos + sectionOffset), round(ypos + sectionOffset), sectionSize, sectionSize));
//fill(noise.get(round(xpos), round(ypos)));
if(debug) {
stroke(255, 0, 255);
strokeWeight(1);
}
fill(c);
ellipse(xpos, ypos, circleSize, circleSize);
if(debug) {
noStroke();
fill(255, 0, 255);
rect(round(xpos + sectionOffset), round(ypos + sectionOffset), sectionSize, sectionSize);
}
}
}
}
color getAvgImgColor(PImage section) {
section.loadPixels();
int avgR = 0, avgG = 0, avgB = 0;
int totalPixels = section.pixels.length;
for (int i = 0; i < totalPixels; i++) {
color pixel = section.pixels[i];
//if(pixel == background) continue;
avgR += red(pixel);
avgG += green(pixel);
avgB += blue(pixel);
}
return color(
round(avgR / totalPixels),
round(avgG / totalPixels),
round(avgB / totalPixels)
);
}
This is what i get when drawing my grid on the image in question:
As you can see in the circled area not all circles should be filled with white... This happens in more places than just the circled are just compare this image with the one below.
I will upload the original image below, so you can use it to debug.
There's a mismatch between the dimensions of your sketch (1038 x 525) and the image you're sampling (2076 x 1048) which might explain the misalignment.
If size(2076, 1048) isn't an option try resizing the image once it's loaded in setup():
...
img = loadImage("accidents.png");
img.resize(width, height);
...
When I run the code it generates 16 rectangles with a random size, random position, and a random color. It is then supposed to turn white if it is colliding with another rectangle. Most of the time it works fine but every so often rectangles turn white when they are not colliding with anything.
Main
int boxCount = 16;
Box[] boxes = new Box[boxCount];
void setup(){
size(500, 500);
for(int i = 0; i < boxCount; i++){
boxes[i] = new Box(random(50, width - 50), random(50, height - 50), random(20, 50), random(20, 50), color(random(0, 255), random(0, 255), random(0, 255)));
}
}
void draw(){
for(int i = 0; i < boxCount; i++){
boxes[i].create();
for(int x = 0; x < boxCount; x++){
if(boxes[i] != boxes[x]){
boxes[i].collide(boxes[x]);
}
}
}
}
Class
class Box{
float x;
float y;
float w;
float h;
color c;
Box(float _x, float _y, float _w, float _h, color _c){
x = _x;
y = _y;
w = _w;
h = _h;
c = _c;
}
void create(){
fill(c);
rect(x, y, w, h);
}
void collide(Box o){
float right = x + (w / 2);
float left = x - (w / 2);
float top = y - (h / 2);
float bottom = y + (h / 2);
float oRight = o.x + (o.w / 2);
float oLeft = o.x - (o.w / 2);
float oTop = o.y - (o.h / 2);
float oBottom = o.y + (o.h / 2);
if(right > oLeft && left < oRight && bottom > oTop && top < oBottom){
c = color(255, 255, 255);
}
}
}
rect doesn't draw a rectangle around center point, by default the rectangle is drawn at a top left position (x, y) with a size (with, height).
You've 2 possibilities to solve the issue:
Either change the collision detection method:
class Box{
// [...]
void collide(Box o){
if(x < o.x+o.w && o.x < x+w && y < o.y+o.h && o.y < y+h){
c = color(255, 255, 255);
}
}
}
Or set the CENTER rectMode(), which will cause the rectangle to be drawn as you expect it:
class Box{
// [...]
void create(){
fill(c);
rectMode(CENTER);
rect(x, y, w, h);
}
// [...]
}
I want to animate multiple paths in sequence, for example, I have
Path[] line and Path[] circle
so when I do for example
line[0].moveTo(x, y);
graphPath[j].lineTo(x, y);
I want to animate this part first and when
finish I want to add circle in same x,y postion
circle[0].addCircle(x, y, 10, Path.Direction.CW);
what I have done so far inside onDraw()
#Override
protected void onDraw(Canvas canvas) {
for (int j = 0; j < 2; j++) {
if (dataPoints2[j] != null) {
float x = leftPadding;
float y = height * getDataPoint(0, j) + topPadding;
graphPath[j].moveTo(x, y);
circlePath[j].addCircle(x, y, 10, Path.Direction.CW);
for (int i = 1; i < dataPoints2[j].size(); i++) {
x = width * (((float) i ) / dataPoints2[j].size()) + leftPadding;
y = height * getDataPoint(i, j) + topPadding;
graphPath[j].lineTo(x, y);
circlePath[j].addCircle(x, y, 10, Path.Direction.CW);
}
}
}
PathMeasure measure = new PathMeasure(graphPath[0], false);
length = measure.getLength();
ObjectAnimator animator = ObjectAnimator.ofFloat(Chart.this, "phase", 1.0f, 0.0f);
animator.setDuration(8000);
animator.start();
for (int j = 0; j < graphPath.length; j++) {
canvas.drawPath(graphPath[j], linePaint[j]);
canvas.drawPath(circlePath[j], circlePaint[j]);
}
}
public void setPhase(float phase){
linePaint[0].setPathEffect(createPathEffect(length, phase, 0.0f));
invalidate();
}
private static PathEffect createPathEffect(float pathLength, float phase, float offset)
{
return new DashPathEffect(new float[] { pathLength, pathLength },
Math.max(phase * pathLength, offset));
}
this will draw the circle then animate the line connecting the circle points
what I need is to animate the line and draw circle when the line reaches every x,y points
I'm writing a Raytracer in Java, I've gotten to the point where I can create objects, rays, test for intersections and then colour pixels. I've also got some basic anti aliasing done. My problem is that if a create a sphere, which should be in the centre of the world (i.e 0.0, 0.0, 0.0) and then draw the image, I end up with a picture like this.
When the red circle should be in the middle of the image.
Main method
public static void main(String[] args) {
System.out.println("Rendering...");
long start = System.nanoTime();
// Setting up the size of the image to be rendered
world = new World(1920, 1080, 1.0);
image = new Image("image.png");
sampler = new SimpleSampler(4);
projector = new OrthographicProjector();
// Main loop of program, goes through each pixel in image and assigns a colour value
for (int y = 0; y < world.viewPlane.height; y++) {
for (int x = 0; x < world.viewPlane.width; x++) {
// Render pixel colour
trace(x, y);
}
}
image.saveImage("PNG");
long end = System.nanoTime();
System.out.print("Loop Time = " + ((end - start)/1000000000.0f));
}
Trace method
public static void trace(int x, int y) {
Colour colour = new Colour();
//int colour = RayTracer.world.backgroundColour.toInteger();
for (int col = 0; col < sampler.samples; col++) {
for (int row = 0; row < sampler.samples; row++) {
Point2D point = sampler.sample(row, col, x, y);
Ray ray = projector.createRay(point);
double min = Double.MAX_VALUE;
Colour tempColour = new Colour();
for (int i = 0; i < world.worldObjects.size(); i++) {
double temp = world.worldObjects.get(i).intersect(ray);
if (temp != 0 && temp < min) {
min = temp;
tempColour = world.worldObjects.get(i).colour;
}
}
colour.add(tempColour);
}
}
colour.divide(sampler.samples*sampler.samples);
image.buffer.setRGB(x, y, colour.toInteger());
}
World.java
public class World {
public ViewPlane viewPlane;
public ArrayList<Renderable> worldObjects;
public Colour backgroundColour;
public World(int width, int height, double size) {
viewPlane = new ViewPlane(width, height, size);
backgroundColour = new Colour(0.0f, 0.0f, 0.0f);
worldObjects = new ArrayList<Renderable>();
worldObjects.add(new Sphere(new Point3D(0.0, 0.0, 0.0), 50, new Colour(1.0f, 0.0f, 0.0f)));
//worldObjects.add(new Sphere(new Point3D(-150.0, 0.0, 0.0), 50, new Colour(1.0f, 0.0f, 0.0f)));
//worldObjects.add(new Sphere(new Point3D(0.0, -540.0, 0.0), 50, new Colour(0.0f, 1.0f, 0.0f)));
}
}
SimpleSampler.java
public class SimpleSampler extends Sampler {
public SimpleSampler(int samples) {
this.samples = samples;
}
public Point2D sample(int row, int col, int x, int y) {
Point2D point = new Point2D(
x - RayTracer.world.viewPlane.width / 2 + (col + 0.5) / samples,
y - RayTracer.world.viewPlane.width / 2 + (row + 0.5) / samples);
return point;
}
}
OrthographicProjector.java
public class OrthographicProjector extends Projector{
public Ray createRay(Point2D point) {
Ray ray = new Ray();
ray.origin = new Point3D(
RayTracer.world.viewPlane.size * point.x,
RayTracer.world.viewPlane.size * point.y,
100);
ray.direction = new Vector3D(0.0, 0.0, -1.0);
return ray;
}
}
I have a feeling that somewhere along the way I've mixed an x with a y and this has rotated the image, but I haven't been able to track down the problem. If you would like to see any more of my code I would be happy to show it.
In SimpleSampler.java:
Point2D point = new Point2D(
x - RayTracer.world.viewPlane.width / 2 + (col + 0.5) / samples,
y - RayTracer.world.viewPlane.width / 2 + (row + 0.5) / samples);
You use width for both coordinates. Maybe you should use width and height.