How do I get this array of circles to appear within the boundary of a parallelogram? - random

I understand how to get an array of circles to appear within a given square space, but how do I get them to appear within irregular shaped spaces. I have a parallelogram outlined that defines the boundaries of where I am trying to get the circles to appear, but no idea how to achieve this. Thanks!
Code from p5.js:
let circles = [];
function setup() {
createCanvas(1200, 1200);
colorMode(HSB,360,100,100,1);
for (let i = 0; i < 500; i++) {
x = random(400, 800);
y = random(400, 800);
d = 10;
circles[i] = new circleClass(x, y, d);
}
}
function draw() {
background(35,13,90,1);
noLoop();
for (let i = 0; i < circles.length; i++) {
circles[i].show();
}
noFill();
stroke(0);
quad(400,600,800,200,800,600,400,1000);
}
class circleClass {
constructor(x, y, d) {
this.x = x;
this.y = y;
this.d = d;
}
show() {
noStroke();
fill(27, 71, 73, 1);
ellipse(this.x, this.y, this.d);
}
}

Seems you want to generate small circles in random positions inside parallelogram.
Take two vectors of parallelogram sides, and generate random points using linear combination of these vectors:
ax = v[1].x - v[0].x (800-400 for your quad)
ay = v[1].y - v[0].y (200-600 for your quad)
bx = v[2].x - v[0].x (and so on)
by = v[2].y - v[0].y
t = random(0..1)
u = random(0..1)
x = v[0].x + ax * t + bx * u
y = v[0].y + ay * t + by * u
where v[] is array of parallelogram vertices
let v = [400,600,800,200,800,600,400,1000]
let ax = v[2] - v[0]
let ay = v[3] - v[1]
let bx = v[4] - v[0]
let by = v[5] - v[0]
for loop {
let t = Math.random()
let u = Math.random()
let x = v[0] + ax * t + bx * u
let y = v[1] + ay * t + by * u
set circle center in x, y
Here's a p5.js version:
function setup() {
createCanvas(600, 600);
background(255);
let v = [200, 300, // top left array indices: [x=0, y=1]
400, 100, // top right array indices: [x=2, y=3]
400, 300, // bottom right array indices: [x=4, y=5]
200, 500]; // bottom left array indices: [x=6, y=7]
// top right - top left
let ax = v[2] - v[0];
let ay = v[3] - v[1];
// bottom left - top left
let bx = v[6] - v[0];
let by = v[7] - v[1];
for(let i = 0; i < 500; i++) {
let t = random(); // interpolation amount on 1 axis
let u = random(); // interpolation amount on the other
// offset by top left point (v[0], v[1])
// interpolate along top left to top right (imagine X axis aligned with parallelogram sides)
// and top left to bottom left (imagine Y axis aligned with parallelogram sides)
let x = v[0] + (ax * t + bx * u);
let y = v[1] + (ay * t + by * u);
// render point
circle(x, y, 10);
}
noFill();
// pass the v array as arguments using the spread operator
quad(...v);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>

This is an addition to MBo's answer above (+1).
The gist of is this section:
let x = v[0] + ax * t + bx * u
let y = v[1] + ay * t + by * u
This might look more familiar as the equation of a line.
In p5.js there's already a very useful lerp() function which takes two numbers and third parameter t between 0.0 and 1.0 and returns a number between the first two (e.g. if t is 0.5 the result will be 1/2 between the numbers, 0.25 would the 1/4 there and .75 would be 3/4, etc.).
Here's a basic example:
let v1, v2, v3, v4;
function setup() {
createCanvas(600, 600);
v1 = createVector(200, 300);
v2 = createVector(400, 100);
v3 = createVector(400, 300);
v4 = createVector(200, 500);
}
function draw(){
background(255);
let t = map(constrain(mouseX, 0, width), 0, width, 0.0, 1.0);
// interpolate each component, x,y between v1 and v2 using horizontal mouse movement
let x = lerp(v1.x, v2.x, t);
let y = lerp(v1.y, v2.y, t);
// preview interpolated position
circle(x, y, 10);
noFill();
stroke(0);
quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
We could use this directly, but it would get tedious to do 8 times (2 coordinates (x,y) for 4 points (quad)).
There's also very useful p5.Vector class in p5 with a p5.Vector.lerp() which can help easily interpolate between two points:
let v1, v2, v3, v4;
function setup() {
createCanvas(600, 600);
v1 = createVector(200, 300);
v2 = createVector(400, 100);
v3 = createVector(400, 300);
v4 = createVector(200, 500);
}
function draw(){
background(255);
let t = map(constrain(mouseX, 0, width), 0, width, 0.0, 1.0);
// interpolate each component, x,y between v1 and v2 using horizontal mouse movement
let l = p5.Vector.lerp(v1, v2, t);
// preview interpolated position
circle(l.x, l.y, 10);
noFill();
stroke(0);
quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
If we group interpolation between pairs of points using two different interpolation amounts into a reusable function we essentially implement a form of bilinear interpolation:
/**
* Bilinear interpolation: interpolates a point on a between two lines (defined by 4 points)
* #param xt: traversal on first axis (0.0 -> 1.0)
* #param yt: traversal on second axis (0.0 -> 1.0)
**/
function quadLerp(v1, v2, v3, v4, xt, yt){
let v1to2 = p5.Vector.lerp(v1, v2, yt);
let v3to4 = p5.Vector.lerp(v3, v4, yt);
return p5.Vector.lerp(v1to2, v3to4, xt);
}
Demo:
let v1, v2, v3, v4;
function setup() {
createCanvas(600, 600);
v1 = createVector(200, 300);
v2 = createVector(400, 100);
v3 = createVector(400, 300);
v4 = createVector(200, 500);
}
/**
* Bilinear interpolation: interpolates a point on a between two lines (defined by 4 points)
* #param xt: traversal on first axis (0.0 -> 1.0)
* #param yt: traversal on second axis (0.0 -> 1.0)
**/
function quadLerp(v1, v2, v3, v4, xt, yt){
let v1to2 = p5.Vector.lerp(v1, v2, yt);
let v3to4 = p5.Vector.lerp(v3, v4, yt);
// text() is simply for debugging/visualisation purposed: not actually required
text("v1to2", v1to2.x, v1to2.y);
text("v3to4", v3to4.x, v3to4.y);
return p5.Vector.lerp(v1to2, v3to4, xt);
}
function draw(){
background(192, 255, 192);
let u = map(constrain(mouseX, 0, width), 0, width, 0.0, 1.0);
let v = map(constrain(mouseY, 0, height), 0, height, 0.0, 1.0);
// vertex order (winding) is important
let l = quadLerp(v1, v2, v4, v3, u, v);
// preview interpolated position
circle(l.x, l.y, 10);
noFill();
stroke(0);
quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Notice as the mouse moves across the whole sketch canvas the circle always stays within the bounds of the quad.
That's pretty much the solution: simply swap the mapped mouse coordinates for random numbers between 0.0 and 1.0.
function setup() {
createCanvas(1200, 1200);
background(192, 255, 192);
let v1 = createVector(400, 600);
let v2 = createVector(800, 200);
let v3 = createVector(800, 600);
let v4 = createVector(400, 1000);
for (let i = 0 ; i < 500; i++) {
let u = random();
let v = random();
// vertex order (winding) is important
let l = quadLerp(v1, v2, v4, v3, u, v);
circle(l.x, l.y, 10);
}
noFill();
stroke(0);
quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}
/**
* Bilinear interpolation: interpolates a point on a between two lines (defined by 4 points)
* #param xt: traversal on first axis (0.0 -> 1.0)
* #param yt: traversal on second axis (0.0 -> 1.0)
**/
function quadLerp(v1, v2, v3, v4, xt, yt){
let v1to2 = p5.Vector.lerp(v1, v2, yt);
let v3to4 = p5.Vector.lerp(v3, v4, yt);
return p5.Vector.lerp(v1to2, v3to4, xt);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Here's a variant closer to your code:
let circles = [];
function setup() {
createCanvas(1200, 1200);
colorMode(HSB,360,100,100,1);
background(35,13,90,1);
let v1 = createVector(400, 600);
let v2 = createVector(800, 200);
let v3 = createVector(800, 600);
let v4 = createVector(400, 1000);
for (let i = 0 ; i < 500; i++) {
let u = random();
let v = random();
// vertex order (winding) is important
let l = quadLerp(v1, v2, v4, v3, u, v);
circles[i] = new Circle(l.x, l.y, 10);
circles[i].show();
}
noFill();
stroke(0);
quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}
/**
* Bilinear interpolation: interpolates a point on a between two lines (defined by 4 points)
* #param xt: traversal on first axis (0.0 -> 1.0)
* #param yt: traversal on second axis (0.0 -> 1.0)
**/
function quadLerp(v1, v2, v3, v4, xt, yt){
let v1to2 = p5.Vector.lerp(v1, v2, yt);
let v3to4 = p5.Vector.lerp(v3, v4, yt);
return p5.Vector.lerp(v1to2, v3to4, xt);
}
class Circle {
constructor(x, y, d) {
this.x = x;
this.y = y;
this.d = d;
}
show() {
noStroke();
fill(27, 71, 73, 1);
ellipse(this.x, this.y, this.d);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
I've made a couple of tweaks:
renamed circleClass to simply Circle keeping the naming convention of a title case class name. I've removed the Class part to avoid potential confusion when instantiating circles (e.g. new circleClass())
I've removed draw(), hence removing the need for noLoop();
the circles array is sort of redundant too in this basic example, but could useful in a more detailed, animated version of this sketch.
Take you time to read/tweak/break/fix the code above.
Understanding the basics will save you so much time on the long run.
It's may appear counter intuitive, but sometimes slowing down helps you speed up. Have fun!

Related

p5.js: make an animated spiral with line instead of ellipse?

I'm trying to animate a spiral using a line, but can only seem to get it to work using ellipses.
Does anyone know how to replace the ellipse() with line()?
here is the code:
var angle = 0.0;
var offset = 60;
var scalar = 10;
var speed = 0.05;
function setup() {
createCanvas(600, 120);
fill(0);
}
function draw() {
var x = offset + cos(angle) * scalar;
var y = offset + sin(angle) * scalar;
ellipse( x, y, 2, 2);
angle += speed;
scalar += speed;
}
Assuming you would like to draw the entire spiral instantaneously using line segments, the you simply need a for loop that calculates the x and y coordinates for the current and next point in the spiral for some increment of change, and then draw lines between each pair of points. There are certainly numerous ways to write such a for loop, depending on what the constrains are (do you want a specific number of rings in your spiral? a specific number of degrees of rotation?), but importantly the bigger your increment of change the less smooth your spiral will look. Here is an example that uses the mouse position to determine the number of rings and the size of the change increments:
function setup() {
createCanvas(windowWidth, windowHeight);
stroke(0);
strokeWeight(4);
textAlign(LEFT, TOP);
}
function draw() {
background(255);
// let the horizontal mouse position indicate the
// size of the steps
let speed = map(mouseX, 0, width, 0.01, 1, true);
// let the vertical mouse position indicate the
// total amount of rotation
let maxRotation = map(mouseY, 0, height, TWO_PI, TWO_PI * 50, true);
push();
noStroke();
fill('red');
text(`Rings: ${(maxRotation / TWO_PI).toFixed(1)}, Speed: ${speed.toFixed(2)}`, 10, 10);
pop();
translate(width / 2, height / 2);
let scalar = 10;
if (speed <= 0) {
console.error('cannot have <= 0 speed');
return;
}
for (let angle = 0; angle < maxRotation; angle += speed, scalar += speed) {
const x = cos(angle) * scalar;
const y = sin(angle) * scalar;
const x2 = cos(angle + speed) * (scalar + speed);
const y2 = sin(angle + speed) * (scalar + speed);
line(x, y, x2, y2);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

Ying and Yang P5.js

I'm currently trying to make a Ying and Yang symbol spin using a circular path. SO far I have made the medium and smaller ones rotate just fine. However, the stationary arc's are wrecking the illusion. Here is an open link to see my current code.
https://editor.p5js.org/Nathan65bmx/sketches/PAu3xx6Bd
Just looking for someone to help me make it look like it is rotating properly.
Draw all shapes from a common central point, then use the rotate() function. https://p5js.org/reference/#/p5/rotate Here's the link.
Do ask if you need help modifying the code.
[EDIT]
Here's the working version
function setup() {
createCanvas(600, 600);
angleMode(DEGREES);
a = 0;
x = 180;
}
let ANGLE = 0
let a;
let x;
function draw() {
background(180, 13, 123);
//Big Circle
noStroke();
//Change starts from here
push();
translate(300, 300);
rotate(a);
fill("black");
arc(0, 0, 300, 300, 0, x);
fill("white")
arc(0, 0, 300, 300, x,0);
pop();
a+=2;
//Till here
// Medium Circles
fill("black");
let CENTRE_X4 = width / 2;
let CENTRE_Y4 = height / 2;
let RADIUS4 = 75;
let X4 = RADIUS4 * cos(ANGLE);
let Y4 = RADIUS4 * sin(ANGLE);
ellipse(CENTRE_X4 + X4, CENTRE_Y4 + Y4, 150);
fill("white");
let CENTRE_X3 = width / 2;
let CENTRE_Y3 = height / 2;
let RADIUS3 = 75;
let X3 = RADIUS3 * cos(ANGLE);
let Y3 = RADIUS3 * sin(ANGLE);
ellipse(CENTRE_X3 - X3, CENTRE_Y3 - Y3, 150);
// Small Circles
fill("white");
let CENTRE_X = width / 2;
let CENTRE_Y = height / 2;
let RADIUS = 75;
let X = RADIUS * cos(ANGLE);
let Y = RADIUS * sin(ANGLE);
ellipse(CENTRE_X + X, CENTRE_Y + Y, 50);
fill("black");
let CENTRE_X2 = width / 2;
let CENTRE_Y2 = height / 2;
let RADIUS2 = 75;
let X2 = RADIUS2 * cos(ANGLE);
let Y2 = RADIUS2 * sin(ANGLE);
ellipse(CENTRE_X2 - X2, CENTRE_Y2 - Y2, 50);
ANGLE = ANGLE + 2;
}
All the edits have been done using the push() & pop() and rotate() functions.
Hope this has helped!
My answer is not adding anything new to Ruskin's great answer suggesting rotate() as well as push()/pop(), but wanted to mention that you could isolate the drawing instructions into a re-usable function and additionally simply reduce some of complexity and repetition (see D.R.Y):
function setup() {
createCanvas(600, 600);
angleMode(DEGREES);
}
function draw() {
background (200, 13, 123);
// isolate coordinate system
push();
// move everything to the center
translate(width / 2, height / 2);
// rotate everything from the center
rotate(frameCount % 360);
// draw ying Yang
drawYingYang(300);
// return to the original coordinate system (0,0 = top left)
pop();
}
function drawYingYang(outerDiameter){
let innerYOffset = outerDiameter / 4;
let outerRadius = outerDiameter / 2;
let innerDiameter = innerYOffset / 1.5;
// Big Circle
noStroke();
fill("black");
arc(0, 0, outerDiameter, outerDiameter, -90, -270);
fill("white")
arc(0, 0, outerDiameter, outerDiameter, 90, 270);
// Medium Circles
fill("black");
ellipse(0, innerYOffset, outerRadius);
fill("white");
ellipse(0, - innerYOffset, outerRadius);
// Small Circles
fill("white");
ellipse(0, innerYOffset, innerDiameter);
fill("black");
ellipse(0, - innerYOffset, innerDiameter);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
If that's the only thing you want to draw, removing push()/pop() won't make a difference visually, however, if you want to draw other shapes it will much easier to have independent control over where each shape is drawn

How to make a crescent moon shape in HTML canvas

I need to make the following shape in HTML5 canvas. I have tried using cubic bezier arcs and also clipping two circles.
How can I make this shape?
Here's my work in progress, just cant get it right
https://codepen.io/matt3224/pen/oeXbdg?editors=1010
var canvas = document.getElementById("canvas1");
var ctx1 = canvas.getContext("2d");
ctx1.lineWidth = 2;
ctx1.beginPath();
ctx1.bezierCurveTo(4, 42, 0, 0, 42, 4);
ctx1.moveTo(4, 42);
ctx1.bezierCurveTo(4, 42, 0, 84, 42, 84);
ctx1.stroke();
var canvas = document.getElementById("canvas2");
var ctx2 = canvas.getContext("2d");
ctx2.lineWidth = 2;
ctx2.beginPath();
ctx2.arc(55, 75, 50, 0, Math.PI * 2, true);
ctx2.moveTo(165, 75);
ctx2.arc(75, 75, 50, 0, Math.PI * 2, true);
ctx2.fill();
Circle circle boolean operation.
Incase anyone is interested in a programmatic solution the example below finds the intercept points of the two circles and uses those points to calculate the start and end angles for the outer and inner circle.
This is a little more flexible than a masking solution as it give you a path.
Snippet shows circle, move mouse over circle to see crescent solution. Not the stroke that would not be available if using a masking solution.
const PI2 = Math.PI * 2;
const ctx = canvas.getContext("2d");
canvas.height = canvas.width = 400;
const mouse = {x : 0, y : 0, button : false}
function mouseEvents(e){
const m = mouse;
const bounds = canvas.getBoundingClientRect();
m.x = e.pageX - bounds.left - scrollX;
m.y = e.pageY - bounds.top - scrollY;
m.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : m.button;
}
["down","up","move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));
// generic circle circle intercept function. Returns undefined if
// no intercept.
// Circle 1 is center x1,y1 and radius r1
// Circle 2 is center x2,y2 and radius r2
// If points found returns {x1,y1,x2,y2} as two points.
function circleCircleIntercept(x1,y1,r1,x2,y2,r2){
var x = x2 - x1;
var y = y2 - y1;
var dist = Math.sqrt(x * x + y * y);
if(dist > r1 + r2 || dist < Math.abs(r1-r2)){
return; // no intercept return undefined
}
var a = (dist * dist - r1 * r1 + r2 *r2) / ( 2 * dist);
var b = Math.sqrt(r2 * r2 - a * a);
a /= dist;
x *= a;
y *= a;
var mx = x2 - x;
var my = y2 - y;
dist = b / Math.sqrt(x * x + y * y);
x *= dist;
y *= dist;
return {
x1 : mx-y,
y1 : my+x,
x2 : mx+y,
y2 : my-x,
};
}
// draws a crescent from two circles if possible
// If not then just draws the first circle
function drawCrescent(x1,y1,r1,x2,y2,r2){
// The circle circle intercept finds points
// but finding the angle of the points does not consider
// the rotation direction and you end up having to do a lot of
// checking (if statments) to determin the correct way to draw each circle
// the following normalises the direction the circle are from each other
// thus making the logic a lot easier
var dist = Math.hypot(x2-x1,y2-y1);
var ang = Math.atan2(y2-y1,x2-x1);
var intercepts = circleCircleIntercept(x1,y1,r1,x1 + dist,y1,r2);
if(intercepts === undefined){
ctx.beginPath();
ctx.arc(x1, y1, r1, 0, PI2);
if(dist < r1){
ctx.moveTo(x2 + r2, y2);
ctx.arc(x2, y2, r2, 0, PI2, true);
}
ctx.fill();
ctx.stroke();
return;
}
// get the start end angles for outer then inner circles
const p = intercepts;
var startA1 = Math.atan2(p.y1 - y1, p.x1 - x1) + ang;
var endA1 = Math.atan2(p.y2 - y1, p.x2 - x1) + ang;
var startA2 = Math.atan2(p.y1 - y1, p.x1 - (x1 + dist)) + ang;
var endA2 = Math.atan2(p.y2 - y1, p.x2 - (x1 + dist)) + ang;
ctx.beginPath();
if(endA1 < startA1){
ctx.arc(x1, y1, r1, startA1, endA1);
ctx.arc(x2, y2, r2, endA2, startA2, true);
}else{
ctx.arc(x2, y2, r2, endA2, startA2);
ctx.arc(x1, y1, r1, startA1, endA1,true);
}
ctx.closePath();
ctx.fill();
ctx.stroke();
}
const outerRadius = 100;
const innerRadius = 80;
var w = canvas.width;
var h = canvas.height;
var cw = w / 2; // center
var ch = h / 2;
var globalTime;
ctx.font = "32px arial";
ctx.textAlign = "center";
ctx.lineJoin = "round";
ctx.lineWidth = 8;
ctx.strokeStyle = "#999";
// main update function
function mainLoop(timer){
globalTime = timer;
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.fillStyle = "black";
ctx.fillRect(0,0,w,h);
ctx.fillStyle = "white";
ctx.fillText("Move mouse over circle",cw,40);
drawCrescent(cw, ch-40, outerRadius, mouse.x, mouse.y, innerRadius);
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
canvas { border : 2px solid black; }
<canvas id="canvas"></canvas>
Solved it using globalCompositeOperation
https://codepen.io/matt3224/pen/oeXbdg?editors=1010

Reflect vector in 3D space

A vector should be reflected when intersecting a mesh. When applying the following formula to reflect a vector, the result is set off. I am using toxiclibs in Processing.
// Get the normal of the face that is intersected.
ReadonlyVec3D n = isect.normal;
// calculate the reflected vector b
// a is the green point in the screenshot
b = a.sub(n.scale(2 * a.dot(n)));
b = b.add(b.getNormalized());
EDIT: When taking into account to create a directional vector by subtracting the last point before the intersection with the intersection, still the reflection is off.
Vec3D id = b.sub(isect.pos);
id.normalize();
b = n.scale(2 * id.dot(n)).sub(id);
I had the question a while back and found a few helpful resources:
Paul Bourke's solution for Line-Plane intersection
Vector reflection on 3D Kingdoms
Here's the snippet I used then:
import toxi.geom.Vec3D;
Vec3D[] face = new Vec3D[3];
float ai = TWO_PI/3;//angle increment
float r = 300;//overall radius
float ro = 150;//random offset
Vec3D n;//normal
Ray r1;
void setup() {
size(500, 500, P3D);
for (int i = 0 ; i < 3; i++) face[i] = new Vec3D(cos(ai * i) * r + random(ro), random(-50, 50), sin(ai * i) * r + random(ro));
r1 = new Ray(new Vec3D(-100, -200, -300), new Vec3D(100, 200, 300));
}
void draw() {
background(255);
lights();
translate(width/2, height/2, -500);
rotateX(map(mouseY, 0, height, -PI, PI));
rotateY(map(mouseX, 0, width, -PI, PI));
//draw plane
beginShape(TRIANGLES);
for (Vec3D p : face) vertex(p.x, p.y, p.z);
endShape();
//normals
Vec3D c = new Vec3D();//centroid
for (Vec3D p : face) c.addSelf(p);
c.scaleSelf(1.0/3.0);
Vec3D cb = face[2].sub(face[1]);
Vec3D ab = face[0].sub(face[1]);
n = cb.cross(ab);//compute normal
n.normalize();
line(c.x, c.y, c.z, n.x, n.y, n.z);//draw normal
pushStyle();
//http://paulbourke.net/geometry/planeline/
//line to plane intersection u = N dot ( P3 - P1 ) / N dot (P2 - P1), P = P1 + u (P2-P1), where P1,P2 are on the line and P3 is a point on the plane
Vec3D P2SubP1 = r1.end.sub(r1.start);
Vec3D P3SubP1 = face[0].sub(r1.start);
float u = n.dot(P3SubP1) / n.dot(P2SubP1);
Vec3D P = r1.start.add(P2SubP1.scaleSelf(u));
strokeWeight(5);
point(P.x, P.y, P.z);//point of ray-plane intersection
//vector reflecting http://www.3dkingdoms.com/weekly/weekly.php?a=2
//R = 2*(V dot N)*N - V
//Vnew = -2*(V dot N)*N + V
//PVector V = PVector.sub(r1.start,r1.end);
Vec3D V = r1.start.sub(P);
Vec3D R = n.scaleSelf(2 * (V.dot(n))).sub(V);
strokeWeight(1);
stroke(0, 192, 0);
line(P.x, P.y, P.z, R.x, R.y, R.z);
stroke(192, 0, 0);
line(r1.start.x, r1.start.y, r1.start.z, P.x, P.y, P.z);
stroke(0, 0, 192);
line(P.x, P.y, P.z, r1.end.x, r1.end.y, r1.end.z);
popStyle();
}
void keyPressed() {
setup();
}//reset
class Ray {
Vec3D start = new Vec3D(), end = new Vec3D();
Ray(Vec3D s, Vec3D e) {
start = s ;
end = e;
}
}
Note that this is a basic proof of concept.
Toxiclibs may already provide Ray/Face classes.
Assuming you have the incident direction id and the normal at the point of intersection n, then the reflection rd is
rd = 2 * dot(n,id) * n - id
where all the vectors are normalized.
In your case, if b is the green point, and isect is the point of intersection then id = b - isect normalized.
So the reflection ray r (assuming it has an origin and a direction) is
r.direction = rd
r.origin = isect
You can also look at this Wikipedia article. https://en.wikipedia.org/wiki/Specular_reflection.

Rotate some elements in an ellipse path

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));

Resources