first post here, and probably an easy one.
I've got the code from Processing's reference site:
float a = 0.0;
float inc = TWO_PI/25.0;
for(int i=0; i<100; i=i+4) {
line(i, 50, i, 50+sin(a)*40.0);
a = a + inc;
}
http://processing.org/reference/sin_.html
However, what I need is a line that follows the curve of a Sin wave, not lines representing points along the curve and ending at the 0 axis. So basically I need to draw an "S" shape with a sin wave equation.
Can someone run me through how to do this?
Thank you in advance,
-Askee
To draw a curve you need to store the previous point's position.
float a = 0.0;
float inc = TWO_PI/25.0;
float prev_x = 0, prev_y = 50, x, y;
for(int i=0; i<100; i=i+4) {
x = i;
y = 50 + sin(a) * 40.0;
line(prev_x, prev_y, x, y);
prev_x = x;
prev_y = y;
a = a + inc;
}
Related
I’m trying to make 6 dots along a line(0, random(height), width, random(height)). The dots should be evenly spaced.
You can use lerp(start, end, t) to linearly interpolate between to values by specifying t: where in between the start/end values you'd like the result to be.
This t value is between 0.0 and 1.0 (normalised value). You can think if of it as percentage. (e.g. 0.0 is at the start (0%) value, 1.0 is at the end value(100%), 0.5 is 50% between the start and end value).
In your case, you would:
store the randomly generated values first (before interpolation)
iterate 6 times, and for each iteration
for each iteration, map the iteration index to the normalised value (t)
Finally, use lerp() by plugging in the from/to values and the t value at the current iteration.
Here's a basic example:
float fromX = 0;
float fromY = random(height);
float toX = width;
float toY = random(height);
int numPoints = 6;
for(int i = 0 ; i < numPoints; i++){
float interpolationAmount = map(i, 0, numPoints - 1, 0.0, 1.0);
float interpolatedX = lerp(fromX, toX, interpolationAmount);
float interpolatedY = lerp(fromY, toY, interpolationAmount);
ellipse(interpolatedX, interpolatedY, 9, 9);
}
Alternatively you can use PVector's lerp() to easiely interpolate between points in 2D (or 3D), without having to interpolate every component:
PVector start = new PVector(0 , random(height));
PVector end = new PVector(width, random(height));
for(float t = 0.0 ; t <= 1.0 ; t += 1.0 / 5){
PVector inbetween = PVector.lerp(start, end, t);
ellipse(inbetween.x, inbetween.y, 9, 9);
}
Update
The slope is the ratio (division) between the difference on Y axis (called rise, Δy = y2 - y1 (E.g. toY - fromY)) and the difference on the X axis (called run, Δx = x2 - x1 (e.g. toX - fromY)).
You can use this difference between start and end points (defining the slope) to draw the points in between.
If you divide this difference into equal sections, each for a point you'd like to draw, then you can multiply it as you iterate and simply translate/offset it from the start position:
// start point
float fromX = 0;
float fromY = random(height);
// end point
float toX = width;
float toY = random(height);
// difference between each component
float diffY = toY - fromY;
float diffX = toX - fromX;
// slope = ratio between Y and X difference
float slope = diffY / diffX;
println("slope as ratio", slope, "as degrees", degrees(atan2(diffY, diffX) + PI));
// start drawing 6 points
int numPoints = 6;
// precalculate a sixth
float sectionIncrement = 1.0 / (numPoints - 1);
for(int i = 0 ; i < 6; i++){
// a sixth incremented (e.g. 1/6 * 0, * 1, *2, ...)
float section = sectionIncrement * i;
// a sixth incremented and mulitplied to the difference
// e.g. 1/6 of slope difference, 2/6 of slope / etc.
// to which we offset the start location (fromX, fromY +)
float x = fromX + (diffX * section);
float y = fromY + (diffY * section);
// render
ellipse(x, y, 9, 9);
}
point(0, random(height))
point(width/5, random(height))
point(width/5*2, random(height))
point(width/5*3, random(height))
point(width/5*4, random(height))
point(width, random(height))
I want to change Processing code to p5.js. I tried to write the code of p5.js, but it cannot be displayed anything on p5.js. The original file and my code are shown below.
This is my code:
function setup(){
createCanvas(400,400);
}
var N = 100;
var cx = [0.000, 1.000, 0.500];
var cy = [0.000, 0.000, 0.866];
var x = 0.0, y = 0.0;
function draw(){
for (var i = 0; i < N; i++) {
nextPoint();
drawPoint();
}
}
function drawPoint(){
strokeWeight(1);
var px = map(x,0,1.0,0,300);
var py = map(y,0,1.0,0,300);
point(px, py);
}
function nextPoint() {
let r = random(3);
x = (x + cx[r]) / 2.0;
y = (y + cy[r]) / 2.0;
}
This is source code:(form processing)
void setup(){
size(400,400);
}
int N = 100;
float[] cx = { 0.000, 1.000, 0.500 };
float[] cy = { 0.000, 0.000, 0.866 };
float x = 0.0, y = 0.0;
void draw(){
for (int i = 0; i < N; i++) {
nextPoint();
drawPoint();
}
}
void drawPoint(){
strokeWeight(1);
float px = map(x,0,1.0,0,300);
float py = map(y,0,1.0,0,300);
point(px, py);
}
void nextPoint() {
int r = (int)random(3);
x = (x + cx[r]) / 2.0;
y = (y + cy[r]) / 2.0;
}
You need to use floor() when you define r. Although JavaScript doesn't have rigid data types for its variables like Java, it still doesn't know what you mean when you say something like cx[1.348]. When you use non-integer values for array access, you get cx[1.348] = NaN, and then when you try to draw a point at NaN, NaN, it doesn't do anything. So your code should say let r = floor(random(3)) on line 23 (you might also consider setting the background color in setup()).
Currently I try to write code for calculating the parts of the screen you can see and those who can't because of objects that block light in 2d, like in Among Us:
The code should run on a processor with very low specs (at least in 2020), the C64. On such a simple CPU it's not possible to do such complex math fast enough for a game, so I came up with an idea: First of all, I make everything tile based, that makes processing easier and also means that I can just change entire characters or their color cells. Then I just write code for the PC in Processing (that's a coding language similar to Java but easier to use) to calculate how rays of light would move (the following graphic should make that more understandable), first just with a rectangle (and a single quadrant):
Then I wrote some completely messy assembler code for using the recorded coordinates to just keep filling the tiles with an inverted character based on the number of the ray currently being drawn on the ray until they hit an object (/ the tile it wants to fill is not inverted and not a space) and then just go to the next ray. I reduced the radius to 7 so it just takes up 256 bytes, useful for ASM. And that totally worked, I was able to fix every single bug and the result was quite impressive, since I needed to add pause statements or everything ran so fast that you couldn't see anything.
After that worked, I tried it with a circle, setting the points using this code:
int pointNum = ceil(radius * PI * 2); // calculates the circumference
for(int i = 0;i < pointNum;i++){
float angle = map(i, 0, pointNum, 0, PI*2);
setPixel(sin(angle) * radius, cos(angle) * radius);
}
I previously used the Bresenham circle algorithm but that didn't quite work so I tried a more simple way. So ...
All the marked black tiles never get hit by any light, which is a pretty big issue, because it wouldn't make much sense in a game that you just can't see those tiles. The code I used, written in Processing, is:
float[] xPoints = new float[0];
float[] yPoints = new float[0];
float[] xPointsT;
float[] yPointsT;
float[] xPointsHad = new float[0];
float[] yPointsHad = new float[0];
int pos = 0;
float interpolPos = 0;
int radius = 12;
float tileSize = 800.0 / (2*radius+1);
String output = " !byte ";
int pointNum = ceil(radius * PI * 2);
void setup() {
size(800, 800);
frameRate(60);
xPointsT = new float[0];
yPointsT = new float[0];
/*for(int i = 0;i <= radius;i++){
setPixel(radius, i);
setPixel(i, radius);
}*/ //Uncomment this and comment the next 4 lines to get the rectangle version
for(int i = 0;i < pointNum;i++){
float angle = map(i, 0, pointNum, 0, PI*2);
setPixel(sin(angle) * radius, cos(angle) * radius);
}
xPoints = concat(xPoints, xPointsT);
yPoints = concat(yPoints, yPointsT);
}
void draw(){
if(interpolPos > radius){
pos++;
interpolPos = 0;
println(output);
output = " !byte ";
}
float x=0, y=0;
float interpolMul = interpolPos / radius;
x = xPoints[pos] * interpolMul;
y = yPoints[pos] * interpolMul;
interpolPos+=1;//sorta the resolution
background(0);
stroke(255);
for(int i = 0;i < 2*radius+1;i++){
for(int j = 0;j < 2*radius+1;j++){
if((round(x) + radius) == i && (round(y) + radius) == j){
fill(0, 255, 0);
if(output != " !byte ")
output += ", ";
output += i-radius;
output += ", ";
output += j-radius;
xPointsHad = append(xPointsHad, i);
yPointsHad = append(yPointsHad, j);
}
else{
int fillVal = 0;
for(int k = 0; k < xPoints.length;k++){
if(round(xPoints[k])+radius == i && round(yPoints[k])+radius == j){
fillVal += 64;
}
}
fill(0, 0, fillVal);
if(fillVal == 0){
for(int k = 0; k < xPointsHad.length;k++){
if(round(xPointsHad[k]) == i && round(yPointsHad[k]) == j){
fill(128, 0, 0);
}
}
}
}
rect(i * tileSize, j * tileSize, tileSize, tileSize);
}
}
strokeWeight(3);
stroke(0, 255, 255, 64);
for(int i = 0;i < xPoints.length;i++){
line((float(radius)+0.5) * tileSize, (float(radius)+0.5) * tileSize, (float(radius)+0.5+xPoints[i]) * tileSize, (float(radius)+0.5+yPoints[i]) * tileSize);
}
strokeWeight(1);
fill(255, 255, 0);
ellipse((x + radius + 0.5) * tileSize, (y + radius + 0.5) * tileSize, 10, 10);
}
void setPixel(float _x, float _y){
for(int i = 0; i < xPoints.length;i++){
if(_x == xPoints[i] && _y == yPoints[i]){
return;
}
}
for(int i = 0; i < xPointsT.length;i++){
if(_x == xPointsT[i] && _y == yPointsT[i]){
return;
}
}
xPointsT = append(xPointsT, _x);
yPointsT = append(yPointsT, _y);
}
(Instructions to get the rectangle are in the code)
Those mentioned tiles seem to be never hit because the rays on them just jump over them, but what can I do to prevent that? You can decrease interpolPos+=x; to hit more tiles because that way your steps are smaller, but that wastes quite some space, so I don't think that's a good solution. Ideally you could also just decrease the number of coordinates you draw to get a smaller vision. Has anyone a good idea how to do that?
You have chosen wrong method to find all touched cells - instead of point-based way you need cell(squares)-based approach - ray intersects rectangle rather than point.
There is article of Amanatides and Woo "A Fast Voxel Traversal Algorithm for Ray Tracing" for 2D.
Practical implementation.
Example:
Quick-made tracing example. Rays emitted from left top corner go to blue points. If ray meets black cell obstacle, it stops. Pink cells are lighted by rays, grey ones are not.
Okay, I found something that worked for me in my situation: I just used the part that totally works (the rectangle) and then just make that a circle by ignoring every tile hit that's further away from the light source then the radius + 0.5, because without + .5 the circle looks weird. You can try it yourself, here's the code:
float[] xPoints = new float[0];
float[] yPoints = new float[0];
float[] xPointsT;
float[] yPointsT;
float[] xPointsHad = new float[0];
float[] yPointsHad = new float[0];
int pos = 0;
float interpolPos = 0;
int radius = 7;
float tileSize = 800.0 / (2*radius+1);
int pointNum = ceil(radius * PI * 2);
String standardOutput = " !align 15,0\n !byte ";
void setup() {
size(800, 800);
frameRate(60);
xPointsT = new float[0];
yPointsT = new float[0];
for(int i = 0;i <= radius;i++){
setPixel(radius, i);
setPixel(i, radius);
} //Uncomment this and comment the next 4 lines to get the rectangle version
/*for(int i = 0;i < pointNum;i++){
float angle = map(i, 0, pointNum, 0, PI*2);
setPixel(sin(angle) * radius, cos(angle) * radius);
}*/
xPoints = concat(xPoints, xPointsT);
yPoints = concat(yPoints, yPointsT);
xPointsT = new float[0];
yPointsT = new float[0];
}
void draw(){
if(interpolPos > radius){
pos++;
interpolPos = 0;
String output = standardOutput;
for(int i = 0;i < radius + 1;i++){
int indexPos = floor(map(i, 0, radius + 1, 0, xPointsT.length));
output += round(xPointsT[indexPos]);
output += ",";
output += round(yPointsT[indexPos]);
if(i < radius){
output += ", ";
}
}
println(output);
xPointsT = new float[0];
yPointsT = new float[0];
}
float x=0, y=0;
float interpolMul = interpolPos / radius;
x = xPoints[pos] * interpolMul;
y = yPoints[pos] * interpolMul;
interpolPos+=1;//sorta the resolution
background(0);
stroke(255);
for(int i = 0;i < 2*radius+1;i++){
for(int j = 0;j < 2*radius+1;j++){
if((round(x) + radius) == i && (round(y) + radius) == j && sqrt(sq(round(x)) + sq(round(y))) < radius + 0.5){
fill(0, 255, 0);
xPointsT = append(xPointsT, i-radius);
yPointsT = append(yPointsT, j-radius);
xPointsHad = append(xPointsHad, i);
yPointsHad = append(yPointsHad, j);
}
else{
int fillVal = 0;
for(int k = 0; k < xPoints.length;k++){
if(round(xPoints[k])+radius == i && round(yPoints[k])+radius == j){
fillVal += 64;
}
}
fill(0, 0, fillVal);
if(fillVal == 0){
for(int k = 0; k < xPointsHad.length;k++){
if(round(xPointsHad[k]) == i && round(yPointsHad[k]) == j){
fill(128, 0, 0);
}
}
}
}
rect(i * tileSize, j * tileSize, tileSize, tileSize);
}
}
strokeWeight(3);
stroke(0, 255, 255, 64);
for(int i = 0;i < xPoints.length;i++){
line((float(radius)+0.5) * tileSize, (float(radius)+0.5) * tileSize, (float(radius)+0.5+xPoints[i]) * tileSize, (float(radius)+0.5+yPoints[i]) * tileSize);
}
strokeWeight(1);
fill(255, 255, 0);
ellipse((x + radius + 0.5) * tileSize, (y + radius + 0.5) * tileSize, 10, 10);
}
void setPixel(float _x, float _y){
for(int i = 0; i < xPoints.length;i++){
if(_x == xPoints[i] && _y == yPoints[i]){
return;
}
}
for(int i = 0; i < xPointsT.length;i++){
if(_x == xPointsT[i] && _y == yPointsT[i]){
return;
}
}
xPointsT = append(xPointsT, _x);
yPointsT = append(yPointsT, _y);
}
Besides the main difference to ignore tiles that are not in the circle, I also changed that I store the coordinates not in a String but in two arrays, because then I use code to stretch them when there are fewer then radius + 1 points, so I don't have to store multiple circles with different sizes in the C64's RAM, so it meets my main requirements: It should fill every tile and it should be downscalable by ignoring some points at the end of rays. And is if efficient? Uh ... there could be a better solution that fills the circle with fewer rays, but I don't care too much. Still, if you have an idea, it would be nice if you could tell me, but otherwise this question is solved.
Edit: I forgot to add a picture. Don't be confused, I modified the code after posting it so you can also see the blue tiles on the circle.
Say I am giving a 2D array which contains black and white pixels.
I want to find the "center" or the datapoints based on the adjacent pixels.
That means the most dense parts have the highest impact, and small loose/scatterd/thinly only have a small impact.
Here is a sample images for my use case:
What is the best algorithm in this scenario to find the center?
The following function calculates the weighted center of a given image.
The image is represented as an array of boolean. Black is represented as 'true' and white as 'false'.
double[] weightedCenter(boolean[][] img){
int W = img.length;
int H = img[0].length;
double centerX = 0;
double centerY = 0;
for(int i=0;i<W;i++){
for(int j=0;j<H;j++){
if(!img[i][j])
continue;
centerX += nbs(img, i, j) * i;
centerY += nbs(img, i, j) * j;
}
}
centerX /= (W * H);
centerY /= (W * H);
return new double[]{centerX, centerY};
}
The weight for each black pixel is calculated(as requested) based on the number of immediate black neighbours.
double nbs(boolean[][] img, int x, int y){
int W = img.length;
int H = img[0].length;
int[] offset = {-1, 0, 1};
double nb0 = 0;
double nb1 = 0;
for(int xOff : offset){
for(int yOff : offset){
int x2 = x + xOff;
int y2 = y + yOff;
if(x2 < 0 || x2 >= W || y2 < 0 || y2 >= H)
continue;
nb0++;
if(img[x2][y2])
nb1++;
}
}
return nb1 / nb0;
}
I am trying to make some objects, say 12, to rotate in an ellipse path continuously in Processing. I got a sketch which does rotation in a circle and I want to make it to rotate in a ellipse. I have some pointer from processing forum but the code from the pointer is different from the code that I posted and I couldn't understand yet (weak in trigonometry).
I googled a bit and found a post trying to achieve this with this algorithm:
You need to define your ellipse with a few parameters:
x, y: center of the ellipse
a, b: semimajor and semiminor axes
If you want to move on the elipses this means that you change the
angle between the major axes and your position on the ellipse. Lets
call this angle alpha.
Your position (X,Y) is:
X = x + (a * Math.cos(alpha));
Y = y + (b * Math.sin(alpha));
In order to move left or right you need to increase/decrease alpha and
then recalculate your position. Source:
http://answers.unity3d.com/questions/27620/move-object-allong-an-ellipsoid-path.html
How do I integrate it into my sketch? Thank you.
Here's my sketch:
void setup()
{
size(1024, 768);
textFont(createFont("Arial", 30));
}
void draw()
{
background(0);
stroke(255);
int cx = 500;
int cy = 350;
int r = 300; //radius of the circle
float t = millis()/4000.0f; //increase to slow down the movement
ellipse(cx, cy, 5, 5);
for (int i = 1 ; i <= 12; i++) {
t = t + 100;
int x = (int)(cx + r * cos(t));
int y = (int)(cy + r * sin(t));
line(cx, cy, x, y);
textSize(30);
text(i, x, y);
if (i == 10) {
textSize(15);
text("x: " + x + " y: " + y, x - 50, y - 20);
}
}
}
Replace
int r = 300; //radius of the circle
with
int a = 350; // major axis of ellipse
int b = 250; // minor axis of ellipse
and replace
int x = (int)(cx + r * cos(t));
int y = (int)(cy + r * sin(t));
with
int x = (int)(cx + a * cos(t));
int y = (int)(cy + b * sin(t));