3d projection stretching Z axis - javascript

I made a 3d projection render in p5.js, but the z axis is very streached
I expected the code to make a perfect cube, but it apers to be a rectangle, If I scale the rectangle by 1, 1, 0.3, it works but I dont know why it is streaching it when I don't do that.
Here is my code:
let t;
let p
let focalLength = 100;
function setup() {
p = createVector(0, 0, 0)
t = createVector(75, 100, 120)
createCanvas(500, 500);
}
function p3d(v, fl){
p.set(v.x*(fl/v.z),
v.y*(fl/v.z),
1*(fl/v.z))
}
function t3d(v, tx, ty, tz){
p.set(v.x + tx,
v.y + ty,
v.z + tz)
}
function s3d(v, sx, sy, sz){
p.set(v.x * sx,
v.y * sy,
v.z * sz)
}
function render(x, y, z){
p.set(x, y, z)
s3d(p, 1, 1, 1);
t3d(p, 100, 100, 100);
p3d(p, 100)
let v = createVector(p.x, p.y)
return v
}
function polygon(v1, v2, v3, v4){
noFill();
//console.log(v1.x)
beginShape();
vertex(v1.x, v1.y);
vertex(v2.x, v2.y);
vertex(v3.x, v3.y);
vertex(v4.x, v4.y);
endShape(CLOSE)
}
function draw() {
background(220);
polygon(render(50, -50, 50),
render(50, 50, 50),
render(-50, 50, 50),
render(-50, -50, 50)
)
polygon(render(50, -50,-50),
render(50, 50, -50),
render(-50, 50, -50),
render(-50, -50, -50)
)
polygon(render(-50, -50, -50),
render(-50, -50, 50),
render(50, -50, 50),
render(50, -50, -50))
polygon(render(50, 50, 50),
render(50, 50, -50),
render(-50, 50, -50),
render(-50, 50, 50))
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>

Related

Changing color of arcs on click

Here is a code I wrote in which I made two different arcs inside a circle. As of now both the arcs changes color at the same time when clicked inside the circle.
I cannot find a way to change the color of the specific arc which is getting clicked on.
Code
function setup() {
createCanvas(500, 500);
r = random(255);
g = random(255);
b = random(255);
}
function draw() {
background(255);
stroke(0);
strokeWeight(10);
fill(r, g, b, 127);
arc(200, 200, 200, 200, HALF_PI, PI*1.75);
stroke(0);
strokeWeight(10);
fill(r-200, g-20, b-55, 200);
arc(200, 200, 200, 200, 5.50, HALF_PI);
fill(255);
ellipse(200, 200, 150, 150);
}
function mousePressed() {
let d = dist(mouseX, mouseY, 200, 200);
if (d < 100) {
// Pick new random color values
r = random(255);
g = random(255);
b = random(255);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
You need 2 colors. I recommend to use color():
let color1, color2;
function setup() {
createCanvas(500, 500);
color1 = color(random(255), random(255), random(255), 127);
color2 = color(random(255), random(255), random(255), 200);
}
function draw() {
// [...]
fill(color1);
arc(200, 200, 200, 200, startAngle, endAngle);
// [...]
fill(color2);
arc(200, 200, 200, 200, endAngle, startAngle);
}
Define a start angle and end angle:
let startAngle = Math.PI/2;
let endAngle = Math.PI*1.75;
Compute the angle of the vector form the center of the arc to the mouse:
let angle = Math.atan2(mouseY - 200, mouseX - 200);
if (angle < 0) angle += Math.PI*2;
Change one of the colors accordingly:
if (d < 100 && d > 75) {
if (angle > startAngle && angle < endAngle) {
color1 = color(random(255), random(255), random(255), 127);
} else {
color2 = color(random(255), random(255), random(255), 127);
}
}
let color1, color2;
let startAngle = Math.PI/2;
let endAngle = Math.PI*1.75;
function setup() {
createCanvas(500, 500);
color1 = color(random(255), random(255), random(255), 127);
color2 = color(random(255), random(255), random(255), 200);
}
function draw() {
background(255);
stroke(0);
strokeWeight(10);
fill(color1);
arc(200, 200, 200, 200, startAngle, endAngle);
stroke(0);
strokeWeight(10);
fill(color2);
arc(200, 200, 200, 200, endAngle, startAngle);
fill(255);
ellipse(200, 200, 150, 150);
}
function mousePressed() {
let d = dist(mouseX, mouseY, 200, 200);
let angle = Math.atan2(mouseY - 200, mouseX - 200);
if (angle < 0) angle += Math.PI*2;
if (d < 100 && d > 75) {
if (angle > startAngle && angle < endAngle) {
color1 = color(random(255), random(255), random(255), 127);
} else {
color2 = color(random(255), random(255), random(255), 127);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>

I am trying to make this "fps" game, and for some reason the mouseClicked function is not working

When i try to do this code for some reason this part of it: (RX1 = random x 1, RY1 = random y 1)(the "+50 part is there because the width of the rectangle is 50)
if(mouseClicked && mouseX > RX1 && mouseX < RX1 + 50 && mouseY > RY1 && mouseY < RY1 + 50){
fill(0, 0, 0);
rect(RX1, RY1, 50, 50);
}
does not work, the program just ignnores it, ive put it everywhere i could think of, and would appreciate someone's help, thanks!
Here is the direct link to it
Here is the rest of the code in case you need it(everything in it works besides the if mouseclicked section
//random x and y variables for targets
var RX1 = random(25, 375);
var RX2 = random(25, 375);
var RX3 = random(25, 375);
var RX4 = random(25, 375);
var RY1 = random(25, 375);
var RY2 = random(25, 375);
var RY3 = random(25, 375);
var RY4 = random(25, 375);
//the juicy stuff
draw = function() {
//coloring
background(255, 255, 255);
fill(255, 0, 0);
//targets:
rect(RX1, RY1, 50, 50);
rect(RX2, RY2, 50, 50);
rect(RX3, RY3, 50, 50);
rect(RX4, RY4, 50, 50);
//gun
fill(0, 0, 0);
//front sight
rect(mouseX - 5, mouseY, 10, 20);
fill(255, 255, 255);
ellipse(mouseX + 0.5, mouseY + 5, 5, 5);
fill(0, 0, 0);
//back sights
rect(mouseX + 10, mouseY, 20, 20);
rect(mouseX - 30, mouseY, 20, 20);
//slide
rect(mouseX - 35, mouseY + 20, 70, 50);
//grip ledge
fill(77, 77, 77);
rect(mouseX - 35, mouseY + 71, 70, 20);
fill(0, 0, 0);
//grip
rect(mouseX - 30, mouseY + 90, 60, 100);
};
//on mouse click/during shooting
if(mouseClicked && mouseX > RX1 && mouseX < RX1 + 50 && mouseY > RY1 && mouseY < RY1 + 50 ){
fill(0, 0, 0);
rect(RX1, RY1, 50, 50);
}
P.S. I am aware i made the last snippet only for one rectangle, not a single one works either way
I think I've gotten the program to respond on a mouse click. The issue seems to be that your if statement that checks for the mouse click is outside of the draw function, which loops forever, so it never checks the if statement. I tried putting that if statement in a mousePressed function (which gets called on a mouse click regardless of where you are in the code), and it seems to work now (or at least it executes the code inside the if statement):
//variables
//random x and y variables for targets
var RX1 = random(25, 375);
var RX2 = random(25, 375);
var RX3 = random(25, 375);
var RX4 = random(25, 375);
var RY1 = random(25, 375);
var RY2 = random(25, 375);
var RY3 = random(25, 375);
var RY4 = random(25, 375);
//the juicy stuff
draw = function() {
//coloring
background(255, 255, 255);
fill(255, 0, 0);
//targets:
rect(RX1, RY1, 50, 50);
rect(RX2, RY2, 50, 50);
rect(RX3, RY3, 50, 50);
rect(RX4, RY4, 50, 50);
//gun
fill(0, 0, 0);
//front sight
rect(mouseX - 5, mouseY, 10, 20);
fill(255, 255, 255);
ellipse(mouseX + 0.5, mouseY + 5, 5, 5);
fill(0, 0, 0);
//back sights
rect(mouseX + 10, mouseY, 20, 20);
rect(mouseX - 30, mouseY, 20, 20);
//slide
rect(mouseX - 35, mouseY + 20, 70, 50);
//grip ledge
fill(77, 77, 77);
rect(mouseX - 35, mouseY + 71, 70, 20);
fill(0, 0, 0);
//grip
rect(mouseX - 30, mouseY + 90, 60, 100);
};
//on mouse click/during shooting
mousePressed = function() {
if(mouseX > RX1 && mouseX < RX1 + 50 && mouseY > RY1 && mouseY < RY1 + 50){
fill(0, 0, 0);
rect(RX1, RY1, 50, 50);
}
};

How to calculate a point on a rotated ellipse

I have working code to find the x,y at a given angle on an ellipse :
getPointOnEllipse(origin_x, origin_y, radius_x, radius_y, angle) {
var r_end_angle = end_angle / 360 * 2 * Math.PI;
var x = origin_x + Math.cos(r_end_angle) * radius_x;
var y = origin_y + Math.sin(r_end_angle) * radius_y;
return {x: x, y: y}
My question is how do I do this calculation if the ellipse is rotated at angle r, as if created by:
ellipse(origin_x, origin_y, radius_x, radius_y, r);
Update: I tried the suggestion below, and it didn't quite work. Here's the original (0) rotation:
And here's after approximately 90-degree rotation:
Here's the code I tried:
/*
* origin_x - Center of the Ellipse X
* origin_y - Center of the Ellipse Y
* radius_x - Radius X
* radius_y - Radius Y
* angle - angle along the ellipse on which to place the handle
* rotation - Angle which the ellipse is rotated
*/
getPointOnEllipse(origin_x, origin_y, radius_x, radius_y, angle, rotation) {
var r_end_angle = (angle + rotation) / 360 * 2 * Math.PI;
var endX = origin_x + Math.cos(r_end_angle) * radius_x;
var endY = origin_y + Math.sin(r_end_angle) * radius_y;
var cosA = Math.cos(rotation);
var sinA = Math.sin(rotation);
var dx = endX - origin_x;
var dy = endY - origin_y;
rotated_x = origin_x + dx * cosA - dy * sinA;
rotated_y = origin_y + dx * sinA + dy * cosA;
Here's some logging:
X 369, Y 233, radiusX 104, radiusY 17, end_angle 0, rotation 0, endX 473, endY 233, cosA 1, sinA 0, dx 104, dy 0, rotated_x 473, rotated_y 233
X 369, Y 233, radiusX 104, radiusY 17, end_angle 90, rotation 0, endX 369, endY 250, cosA 1, sinA 0, dx 0, dy 17, rotated_x 369, rotated_y 250
X 369, Y 233, radiusX 104, radiusY 17, end_angle 180, rotation 0, endX 265, endY 233, cosA 1, sinA 0, dx -104, dy 0, rotated_x 265, rotated_y 233
X 369, Y 233, radiusX 104, radiusY 17, end_angle 270, rotation 0, endX 369, endY 216, cosA 1, sinA 0, dx 0, dy -17, rotated_x 369, rotated_y 216
Here after a 90-degree rotation the points don't seem to end up on the ellipse:
X 369, Y 233, radiusX 104, radiusY 17, end_angle 0, rotation 96.40608527543233, endX 357.396254311691, endY 249.89385326910204, cosA -0.5542897094655916, sinA 0.8323238059676955, dx -11.603745688309004, dy 16.89385326910204, rotated_x 361.3706805758866, rotated_y 213.97783720494053
X 369, Y 233, radiusX 104, radiusY 17, end_angle 90, rotation 96.40608527543233, endX 265.6493682360816, endY 231.10323387787258, cosA -0.5542897094655916, sinA 0.8323238059676955, dx -103.35063176391839, dy -1.896766122127417, rotated_x 427.86491525130737, rotated_y 148.03016676384783
X 369, Y 233, radiusX 104, radiusY 17, end_angle 180, rotation 96.40608527543233, endX 380.603745688309, endY 216.10614673089796, cosA -0.5542897094655916, sinA 0.8323238059676955, dx 11.603745688309004, dy -16.89385326910204, rotated_x 376.6293194241134, rotated_y 252.02216279505947
X 369, Y 233, radiusX 104, radiusY 17, end_angle 270, rotation 96.40608527543233, endX 472.35063176391833, endY 234.89676612212745, cosA -0.5542897094655916, sinA 0.8323238059676955, dx 103.35063176391833, dy 1.8967661221274454, rotated_x 310.1350847486927, rotated_y 317.969833236
I'm sure I got something wrong here - any ideas?
You actually may not need to calculate this position.
The canvas API offers means to control the current transformation matrix of your context.
In many cases, this is way more convenient to embrace this than to calculate everything yourself.
For instance, your example places the four squares relatively to the ellipse own transformation. So what you need to do, is to first set your transformation matrix to this ellipse's position, and then to only move it by the relative position of each squares.
Here is a fast written example:
const ctx = canvas.getContext('2d');
class Shape {
constructor(cx, cy, parent) {
this.cx = cx;
this.cy = cy;
this.parent = parent;
this.path = new Path2D();
this.angle = 0;
this.color = "black";
}
applyTransform() {
if (this.parent) { // recursively apply all the transforms
this.parent.applyTransform();
}
ctx.transform(1, 0, 0, 1, this.cx, this.cy);
ctx.rotate(this.angle);
}
}
const rad_x = (canvas.width / 1.3) / 2;
const rad_y = (canvas.height / 1.3) / 2;
class Rect extends Shape {
constructor(dx, dy, parent) {
super(rad_x * dx, rad_y * dy, parent);
this.path.rect(-5, -5, 10, 10);
Object.defineProperty(this, 'angle', {
get() {
// so the squares are not rotated
return parent.angle * -1;
}
})
}
}
const ellipse = new Shape(canvas.width / 2, canvas.height / 2);
ellipse.path.ellipse(0, 0, rad_x, rad_y, 0, 0, Math.PI * 2);
const shapes = [ellipse].concat(
[
new Rect(0, -1, ellipse),
new Rect(1, 0, ellipse),
new Rect(0, 1, ellipse),
new Rect(-1, 0, ellipse)
]
);
const mouse = {x:0, y:0};
canvas.onmousemove = ({offsetX, offsetY}) => {
mouse.x = offsetX;
mouse.y = offsetY;
};
draw();
function clearTransform() {
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
function draw() {
// update ellipse's angle
ellipse.angle = (ellipse.angle + Math.PI / 180) % (Math.PI * 2);
// clear
clearTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height)
// draw the shapes
shapes.forEach(shape => {
clearTransform(); // clear the transform matrix completely
shape.applyTransform(); // will apply their parent's transform too
// check if we are hovering this shape
shape.color = ctx.isPointInPath(shape.path, mouse.x, mouse.y) ? 'red' : 'black';
ctx.strokeStyle = shape.color;
ctx.stroke(shape.path);
});
// do it again
requestAnimationFrame(draw);
}
<canvas id="canvas"></canvas>
Just rotate point you got with your function around center of an ellipse:
function rotatePoint(x, y, originX, originY, rotation) {
const cosA = Math.cos(rotation);
const sinA = Math.sin(rotation);
const dx = x - originX;
const dy = y - originY;
return {
x: originX + dx * cosA - dy * sinA,
y: originY + dx * sinA + dy * cosA
}
}
Please note that your function getPointOnEllipse does not return point corresponding to central angle.

Difficulty with javascript library: this.ctx is undefined

So I have been working on a neural network, and I was cleaning up the code earlier today so that I can include it in 2 libraries (as objects) so that I do not have to worry about graphics, and can worry about the actual logic. I have checked JSHint.com (A version of JSLint, basically), and there seems to be nothing wrong with my code. However, when I try to run my code, I get the error this.ctx is undefined. I tried to remove the this keyword, but got ctx is undefined. Here is a copy of my code. I was running it in a basic template with a canvas already created, so there was no need for the first method in the init function. So my questions are:
• Why is this.ctx undefined?
• Why does this work when I do init, drawNodes, and drawNodeConnectors from the console?
• Are there any ways that I can avoid using the this keyword ever time I reference ctx besides a global variable?
var graphics = {
init: function(overlay){
if(!overlay){
this.newCanvas = document.createElement('canvas');
this.newCanvas.id = "canvas";
this.width = 640;
this.height = 360;
document.body.appendChild(this.canvas);
}
this.canvas = document.getElementById('canvas');
this.ctx = this.canvas.getContext('2d');
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.pi = Math.PI;
},
blackout: function(){
this.ctx.fillStyle = "black";
this.ctx.globalAlpha = 1;
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
},
drawCircle: function(x, y, radius){
this.ctx.beginPath();
this.ctx.fillStyle = "white";
this.ctx.strokeStyle = "white";
this.ctx.globalAlpha = 1;
this.ctx.arc(x, y, radius, 0, this.pi*2);
this.ctx.stroke();
this.ctx.fill();
},
drawLine: function(x1, y1, x2, y2, alpha){
this.ctx.beginPath();
this.ctx.strokeStyle = "white";
this.ctx.lineWidth = 5;
this.ctx.globalAlpha = alpha;
this.ctx.moveTo(x1, y1);
this.ctx.lineTo(x2, y2);
this.ctx.stroke();
this.ctx.closePath();
},
drawText: function(x, y, text, size){
this.ctx.font = size+"px Times";
this.ctx.fillText(text, x, y);
},
textOffset: function(text, size){
this.ctx.font = size+"px Times";
return this.ctx.measureText( text.toString() ).width / 2;
},
drawNodes: function(){
this.drawCircle(100, 100, 50);
this.drawCircle(100, 260, 50);
this.drawCircle(300, 75, 25);
this.drawCircle(300, 180, 25);
this.drawCircle(300, 285, 25);
this.drawCircle(500, 180, 75);
},
genWeights: function(){
for(this.i = 1; this.i < 9; this.i ++){
eval("this.w" + this.i + " = Math.random();");
}
},
drawNodeConnectors: function(){
this.ctx.fillStyle = "white";
this.drawLine(100, 100, 300, 75, this.w1);
this.drawLine(100, 100, 300, 180, this.w2);
this.drawLine(100, 100, 300, 285, this.w3);
this.drawLine(100, 260, 300, 75, this.w4);
this.drawLine(100, 260, 300, 180, this.w5);
this.drawLine(100, 260, 300, 285, this.w6);
this.drawLine(300, 75, 500, 180, this.w7);
this.drawLine(300, 180, 500, 180, this.w8);
this.drawLine(300, 285, 500, 180, this.w9);
},
nodeText: function(in1, in2, node1, node2, node3, output1, output2){
this.ctx.fillStyle = "black";
this.ctx.globalAlpha = 1;
this.text(100 - this.textOffset(in1, 36), 100, in1.toString(), 36);
this.text(100 - this.textOffset(in2, 36), 260, in2.toString(), 36);
this.text(300 - this.textOffset(node1, 20), 75, node1.toString(), 20);
this.text(300 - this.textOffset(node2, 20), 180, node2.toString(), 20);
this.text(300 - this.textOffset(node3, 20), 285, node3.toString(), 20);
this.text(500 - this.textOffset(output1, 100), 180, output1.toString(), 100);
this.text(500 - this.textOffset(output2, 25), 225, output2.toString(), 25);
},
test: function(){
this.blackout();
this.init(true);
this.drawNodes();
this.genWeights();
this.drawNodeConnectors();
}
};
graphics.test();
You call blackout first and than you call init second. init is where it defines ctx.

Rotate rectangle with math

I want to rotate rectangle like this:
I don't want to use save(), translate(), rotate() and restore(). I want to use simple math to do it. Here is an example what I did already:
demo
How to achieve that?
So you basically need to calculate each corner and draw the rectangle using .lineTo() instead of .fillRect().
To rotate a point around origin by angle you need this:
var rotate = function( point, origin, angle) {
angle = angle * Math.PI / 180.0;
return {
x: Math.cos(angle) * (point.x-origin.x) - Math.sin(angle) * (point.y -origin.y) + origin.x,
y: Math.sin(angle) * (point.x-origin.x) + Math.cos(angle) * (point.y-origin.y) + origin.y
};
}
Then define your rectangle corners:
var corners = [
{ x: 100, y: 0 },
{ x: 150, y: 0 },
{ x: 150, y: 50 },
{ x: 100, y: 50 }
];
And render it like this:
// for example:
// origin = { x: 125, y: 175 }, angle = 30;
ctx.beginPath();
corners.forEach( function( point ){
var rotated = rotate( point, origin, angle );
ctx.lineTo( rotated.x, rotated.y );
});
ctx.closePath();
ctx.fill();
http://jsfiddle.net/grcbab4h/3/

Categories

Resources