How to align object with drop shape function (Piriform Curve) - three.js

I am trying to place the objects based on an equation, so let us say that I want to draw a circle.
Please check my codesandbox
so I use this function to get the coordinates of x and y..
const getCircleCoordinates = (angle, distance = 25) => {
angle *= Math.PI / 180
let x = distance * Math.cos(angle),
y = distance * Math.sin(angle)
return { x, y, angle, distance }
}
and this is my loop to place the objects.
let i = -1
const theta = 360 / (36)
return <>
<axesHelper args={[5, 5, 5]} />
<Center>
<group scale={scale}>
{[...Array(36)].map((e, index) => {
i++
let { x, y, angle } = getCircleCoordinates(i * theta, hight, width)
return <mesh
key={index}
scale-z={z}
position-x={x}
position-y={y}
rotation-x={Math.PI / 2}
rotation-y={angle}
lookAt={[0, 0, 0]}
>
<torusGeometry args={[radius, tube, radialSeg, tubSeg]} />
<meshNormalMaterial
color="red"
// wireframe
/>
</mesh>
})}
</group>
</Center>
</>
Now my issue is with the rotation, in the circle the rotation was simply the angle in radian, but what If I want to draw a square or triangle?
In my case, I want to draw a drop..
and this is the function...
const getDropCoordinates = (angle, hight = 15, width = 5) => {
angle *= Math.PI / 180
let x = width * Math.cos(angle) * (1 + Math.sin(angle)),
y = hight * (1 + Math.sin(angle))
return { x, y, angle }
}

Related

How to draw dots in the coordinates of slices in a d3 pie chart?

I want to draw dot circle in each slice's endpoints. I have attached what I have drawn and what I want to do.
Here is a function to compute the coordinates of a point ( relatively to the center of the piechart ) by percent ( 0 to 100 ) and piechart radius:
const getPoint = (radius, percent) => {
const angle = percent / 100 * Math.PI * 2;
const x = radius * Math.sin(angle);
const y = radius * -Math.cos(angle);
return {x, y};
}
I have solved this problem. the ans is:
transform: function (d) {
const x = radius * Math.sin(d.startAngle - Math.PI * 2);
const y = radius * -Math.cos(d.startAngle - Math.PI * 2);
return "translate(" + x +","+ y + ")";
}

How to rotate cartesian coordinates relative to a vector?

I'm building a fractal tree in three dimensions. I need to draw each generation of branches at an angle relative to the previous generation. The branches are currently drawn at the same angle and are growing "straight up". I know I need to do a rotation of some kind, but not sure if it's quaternions or if I need to take a completely different approach.
Here's a jsfiddle of the fractal tree with the branches growing "straight up".
https://jsfiddle.net/degraeve/xa8m5Lcj/59/
Here's a 2D image of what I'm trying to achieve with the branch angles: https://i.imgur.com/uVK4Dx6.png
code that appears in the jsfiddle:
function draw_tree_branch(x, y, z, phi, theta, radius) {
// use sperical coordinate system
// https://en.wikipedia.org/wiki/Spherical_coordinate_system
var phi_in_degrees = phi * (180 / Math.PI);
var material = new THREE.LineBasicMaterial({
color: 0x00ffff,
linewidth: 1
});
// draw 3 lines at 120 degrees to each other
var angle_between_branches = 120;
var num_branches = 360 / angle_between_branches;
for (var temp_count = 1; temp_count <= num_branches; temp_count++) {
phi_in_degrees += angle_between_branches;
phi = (phi_in_degrees) * Math.PI / 180;
// compute Cartesian coordinates
var x2 = x + (radius * Math.sin(theta) * Math.sin(phi));
var y2 = y + (radius * Math.cos(theta));
var z2 = z + (radius * Math.sin(theta) * Math.cos(phi));
// ????????
// How do I rotate this line so the angles are "relative" to the parent line instead of growing "straight up?"
// Quaternion ???
// example of what I'm trying to achieve, but in 3D:
// https://www.codheadz.com/2019/06/30/Trees-with-Turtle-in-Python/simple_tree.png
// ????????
var points = [];
var vector_1 = new THREE.Vector3(x, y, z);
points.push(vector_1);
var vector_2 = new THREE.Vector3(x2, y2, z2);
points.push(vector_2);
var geometry = new THREE.BufferGeometry().setFromPoints(points);
var line = new THREE.Line(geometry, material);
scene.add(line);
// keep drawing branches until the branch is "too short"
if (radius > 2) {
draw_tree_branch(x2, y2, z2, phi, theta, radius * 0.5);
}
}
}
I may not even be asking the right question. Any pointers in the right direction are appreciated.
You're very close. The only problem is that theta is the same on each iteration, so you'll always get a sub-branch that's 30º from vertical. A simple way to solve this is by keeping track of the iteration you're in, and multiply that by tree_theta so you get an increasing number of degrees: 30, 60, 90, 120, etc...
function draw_tree_branch(x, y, z, phi, tree_theta, radius, iteration) {
var theta = tree_theta * iteration;
// ... perform all calculations
// Draw next branch with iteration + 1
if (radius > 2) {
draw_tree_branch(x2, y2, z2, phi, tree_theta, radius * 0.5, iteration + 1);
}
}
Here's an updated version of your JSFiddle: https://jsfiddle.net/marquizzo/r2w7oz6x/

How to a make a curved sheet (cube) in OpenSCAD?

How can I curve a sheet (cube)? I'd like to control the angle of the bend/curve.
e.g.
cube([50,50,2]);
You can rotate_extrude() an rectangle with the parameter angle. This requires the openscad version 2016.xx or newer, see documentation.
It is necessary to install a development snapshot, see download openscad
$fn= 360;
width = 10; // width of rectangle
height = 2; // height of rectangle
r = 50; // radius of the curve
a = 30; // angle of the curve
rotate_extrude(angle = a) translate([r, 0, 0]) square(size = [height, width], center = true);
looks like this:
The curve is defined by radius and angle. I think it is more realistic, to use other dimensions like length or dh in this sketch
and calculate radius and angle
$fn= 360;
w = 10; // width of rectangle
h = 2; // height of rectangle
l = 25; // length of chord of the curve
dh = 2; // delta height of the curve
module curve(width, height, length, dh) {
// calculate radius and angle
r = ((length/2)*(length/2) - dh*dh)/(2*dh);
a = asin((length/2)/r);
rotate_extrude(angle = a) translate([r, 0, 0]) square(size = [height, width], center = true);
}
curve(w, h, l, dh);
Edit 30.09.2019:
considering comment of Cfreitas, additionally moved the resulting shape to origin, so dimensions can be seen on axes of coordinates
$fn= 360;
w = 10; // width of rectangle
h = 2; // height of rectangle
l = 30; // length of chord of the curve
dh = 4; // delta height of the curve
module curve(width, height, length, dh) {
r = (pow(length/2, 2) + pow(dh, 2))/(2*dh);
a = 2*asin((length/2)/r);
translate([-(r -dh), 0, -width/2]) rotate([0, 0, -a/2]) rotate_extrude(angle = a) translate([r, 0, 0]) square(size = [height, width], center = true);
}
curve(w, h, l, dh);
and the result:
Edit 19.09.2020: There was a typo in the last edit: In the first 'translate' the local 'width' should be used instead of 'w'. Corrected it in the code above.
I can do it this way but it would be better if you could specify the bend/curve in #degrees as an argument to the function:
$fn=300;
module oval(w, h, height, center = false) {
scale([1, h/w, 1]) cylinder(h=height, r=w, center=center);
}
module curved(w,l,h) {
difference() {
oval(w,l,h);
translate([0.5,-1,-1]) color("red") oval(w,l+2,h+2);
}
}
curved(10,20,30);
Using the concept used by a_manthey_67, corrected the math and centered (aligned the chord with y axis) the resulting object:
module bentCube(width, height, length, dh) {
// calculate radius and angle
r = (length*length + 4*dh*dh)/(8*dh);
a = 2*asin(length/(2*r));
translate([-r,0,0]) rotate([0,0,-a/2])
rotate_extrude(angle = a) translate([r, 0, 0]) square(size = [height, width], center = true);}
Or, if you just want something with a fixed length, and a certain bent angle do this:
module curve(width, height, length, a) {
if( a > 0 ) {
r = (360 * (length/a)) / (2 * pi);
translate( [-r-height/2,0,0] )
rotate_extrude(angle = a)
translate([r, 0, 0])
square(size = [height, width], center = false);
} else {
translate( [-height/2,0,width] )
rotate( a=270, v=[1,0,0] )
linear_extrude( height = length )
square(size = [height, width], center = false);
}
}
The if (a > 0) statement is needed to make an exception when the bending angle is 0 (which, if drawing a curved surface, would result in an infinite radius).
Animated GIF here

Developing an Algorithm to Transform Four Cartesian Coordinates Into Square Coordinates

I am working on a project where four randomly placed robots each have unique Cartesian coordinates. I need to find a way to transform these coordinates into the coordinates of a square with side length defined by the user of the program.
For example, let's say I have four coordinates (5,13), (8,17), (13,2), and (6,24) that represent the coordinates of four robots. I need to find a square's coordinates such that the four robots are closest to these coordinates.
Thanks in advance.
As far as I understand your question you are looking for the centroid of the four points, the point which has equal — and thus minimal — distance to all points. It is calculated as the average for each coordinate:
The square's edge length is irrelevant to the position, though.
Update
If you additionally want to minimize the square corners' distance to a robot position, you can do the following:
Calculate the centroid c like described above and place the square there.
Imagine a circle with center at c and diameter of the square's edge length.
For each robot position calculate the point on the circle with shortest distance to the robot and use that as a corner of the square.
It looks as if the original poster is not coming back to share his solution here, so I'll post what I was working on.
Finding the center point of the four robots and then drawing the square around this point is indeed a good way to start, but it doesn't necessarily give the optimal result. For the example given in the question, the center point is (8,14) and the total distance is 22.688 (assuming a square size of 10).
When you draw the vector from a corner of the square to the closest robot, this vector shows you in which direction the square should move to reduce the distance from that corner to its closest robot. If you calculate the sum of the direction of these four vectors (by changing the vectors to size 1 before adding them up) then moving the square in the resulting direction will reduce the total distance.
I dreaded venturing into differential equation territory here, so I devised a simple algorithm which repeatedly calculates the direction to move in, and moves the square in ever decreasing steps, until a certain precision is reached.
For the example in the question, the optimal location it finds is (10,18), and the total distance is 21.814, which is an improvement of 0.874 over the center position (assuming a square size of 10).
Press "run code snippet" to see the algorithm in action with randomly generated positions. The scattered green dots are the center points that are considered while searching the optimal location for the square.
function positionSquare(points, size) {
var center = {x: 0, y:0};
for (var i in points) {
center.x += points[i].x / points.length;
center.y += points[i].y / points.length;
}
paintSquare(canvas, square(center), 1, "#D0D0D0");
order(points);
textOutput("<P>center position: " + center.x.toFixed(3) + "," + center.y.toFixed(3) + "<BR>total distance: " + distance(center, points).toFixed(3) + "</P>");
for (var step = 1; step > 0.0001; step /= 2)
{
var point = center;
var shortest, dist = distance(center, points);
do
{
center = point;
shortest = dist;
var dir = direction();
paintDot(canvas, center.x, center.y, 1, "green");
point.x = center.x + Math.cos(dir) * step;
point.y = center.y + Math.sin(dir) * step;
dist = distance(point, points);
}
while (dist < shortest)
}
textOutput("<P>optimal position: " + center.x.toFixed(3) + "," + center.y.toFixed(3) + "<BR>total distance: " + distance(point, points).toFixed(3) + "</P>");
return square(center);
function order(points) {
var clone = [], best = 0;
for (var i = 0; i < 2; i++) {
clone[i] = points.slice();
for (var j in clone[i]) clone[i][j].n = j;
if (i) {
clone[i].sort(function(a, b) {return b.y - a.y});
if (clone[i][0].x > clone[i][1].x) swap(clone[i], 0, 1);
if (clone[i][2].x < clone[i][3].x) swap(clone[i], 2, 3);
} else {
clone[i].sort(function(a, b) {return a.x - b.x});
swap(clone[i], 1, 3);
if (clone[i][0].y < clone[i][3].y) swap(clone[i], 0, 3);
if (clone[i][1].y < clone[i][2].y) swap(clone[i], 1, 2);
}
}
if (distance(center, clone[0]) > distance(center, clone[1])) best = 1;
for (var i in points) points[i] = {x: clone[best][i].x, y: clone[best][i].y};
function swap(a, i, j) {
var temp = a[i]; a[i] = a[j]; a[j] = temp;
}
}
function direction() {
var d, dx = 0, dy = 0, corners = square(center);
for (var i in points) {
d = Math.atan2(points[i].y - corners[i].y, points[i].x - corners[i].x);
dx += Math.cos(d);
dy += Math.sin(d);
}
return Math.atan2(dy, dx);
}
function distance(center, points) {
var d = 0, corners = square(center);
for (var i in points) {
var dx = points[i].x - corners[i].x;
var dy = points[i].y - corners[i].y;
d += Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
}
return d;
}
function square(center) {
return [{x: center.x - size / 2, y: center.y + size / 2},
{x: center.x + size / 2, y: center.y + size / 2},
{x: center.x + size / 2, y: center.y - size / 2},
{x: center.x - size / 2, y: center.y - size / 2}];
}
}
// PREPARE CANVAS
var canvas = document.getElementById("canvas");
canvas.width = 200; canvas.height = 200;
canvas = canvas.getContext("2d");
// GENERATE TEST DATA AND RUN FUNCTION
var points = [{x:5, y:13}, {x:8, y:17}, {x:13, y:2}, {x:6, y:24}];
for (var i = 0; i < 4; i++) {
points[i].x = 1 + 23 * Math.random(); points[i].y = 1 + 23 * Math.random();
}
for (var i in points) textOutput("point: " + points[i].x.toFixed(3) + "," + points[i].y.toFixed(3) + "<BR>");
var size = 10;
var square = positionSquare(points, size);
// SHOW RESULT ON CANVAS
for (var i in points) {
paintDot(canvas, points[i].x, points[i].y, 5, "red");
paintLine(canvas, points[i].x, points[i].y, square[i].x, square[i].y, 1, "blue");
}
paintSquare(canvas, square, 1, "green");
function paintDot(canvas, x, y, size, color) {
canvas.beginPath();
canvas.arc(8 * x, 200 - 8 * y, size, 0, 6.2831853);
canvas.closePath();
canvas.fillStyle = color;
canvas.fill();
}
function paintLine(canvas, x1, y1, x2, y2, width, color) {
canvas.beginPath();
canvas.moveTo(8 * x1, 200 - 8 * y1);
canvas.lineTo(8 * x2, 200 - 8 * y2);
canvas.strokeStyle = color;
canvas.stroke();
}
function paintSquare(canvas, square, width, color) {
canvas.rect(8 * square[0].x , 200 - 8 * square[0].y, 8 * size, 8 * size);
canvas.strokeStyle = color;
canvas.stroke();
}
// TEXT OUTPUT
function textOutput(t) {
var output = document.getElementById("output");
output.innerHTML += t;
}
<BODY STYLE="margin: 0; border: 0; padding: 0;">
<CANVAS ID="canvas" STYLE="width: 200px; height: 200px; float: left; background-color: #F8F8F8;"></CANVAS>
<DIV ID="output" STYLE="width: 400px; height: 200px; float: left; margin-left: 10px;"></DIV>
</BODY>
Further improvements: I haven't yet taken into account what happens when a corner and a robot are in the same spot, but the overall position isn't optimal. Since the direction from the corner to the robot is undefined, it should probably be taken out of the equation temporarily.

circle rotated rectangle collision detection

I am trying to implement the collision detection between rotated rectangle and circle by following this http://www.migapro.com/circle-and-rotated-rectangle-collision-detection/
I have added the code in jsfiddle here http://jsfiddle.net/Z6KSX/2/.
What am i missing here ?
function check_coll ( circle_x,circle_y, rect_x, rect_y, rect_width, rect_height, rect_angle)
{
// Rotate circle's center point back
var rect_centerX = rect_x /2 ;
var rect_centerY = rect_y /2 ;
var cx = (Math.cos(rect_angle) * (circle_x - rect_centerX)) - (Math.sin(rect_angle) * (circle_y - rect_centerY)) + rect_centerX;
var cy = (Math.sin(rect_angle) * (circle_x - rect_centerX)) + (Math.cos(rect_angle) * (circle_y - rect_centerY)) + rect_centerY;
// Closest point
var x, y;
// Find the unrotated closest x point from center of unrotated circle
if (cx < rect_x) {
x = rect_x;
}
else if (cx > rect_x + rect_width){
x = rect_x + rect_width;
}
else{
x = cx;
}
// Find the unrotated closest y point from center of unrotated circle
if (cy < rect_y){
y = rect_y;
}
else if (cy > rect_y + rect_height) {
y = rect_y + rect_height;
}
else {
y = cy;
}
// Determine collision
var collision = false;
var c_radius = 5;
var distance = findDistance(cx, cy, x, y);
if (distance < c_radius) {
collision = true; // Collision
}
else {
collision = false;
}
return collision;
}
function findDistance (x1, y1, x2, y2) {
var a = Math.abs(x1 - x2);
var b = Math.abs(y1 - y2);
var c = Math.sqrt((a * a) + (b * b));
return c;
}
Hehe, I find this amusing as I somewhat recently solved this for myself after spending a large amount of time going down the wrong path.
Eventually I figured out a way:
1.) Simply rotate the point of the center of the circle by the Negative amount the rectangle has been rotated by. Now the point is 'aligned' with the rectangle (in the rectangles relative coordinate space).
2.) Solve for circle vs. AABB. The way I solved it gave me a point on the rectangle that is closest to the circle's center.
3.) Rotate the resulting point from by the Positive amount the rectangle has been rotated by. Continue solving as usual (checking if the distance between that point and the circle center is within the circle's radius)
From a very quick glance at your code, it seems like maybe you are doing the same thing, but missing the last step? I suggest drawing out your point on the rectangle from step 2 to see exactly where it is to help debug.
I was able to figure this out . The issue in the code was, I was using the wrong radius and had missed the center of rect_x and rect_y
var rect_centerX = rect_x + (rect_width / 2);
var rect_centerY = rect_y + (rect_height /2);
When dealing with rotation on the canvas we will need to add the translate values to the corresponding x and y values used in createrect.
I also use this code for my project and it's working. The only thing you need to do is use -angle instead of the angle.
Here is my code link
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const rectX = 100;
const rectY = 100;
const rectWidth = 200;
const rectHeight = 100;
const circleRadius = 2;
const rectMidPointX = rectX + rectWidth / 2;
const rectMidPointY = rectY + rectHeight / 2;
const angle = Math.PI / 4;
let circleX;
let circleY;
canvas.addEventListener('mousemove', (e) => {
circleX = e.clientX;
circleY = e.clientY;
ctx.save();
ctx.beginPath();
ctx.fillStyle = '#fff';
ctx.arc(circleX, circleY, circleRadius, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.restore();
calculateIntersection();
})
ctx.save();
//ctx.fillRect(100, 100, 100, 100);
ctx.strokeStyle = 'black';
ctx.translate(rectMidPointX, rectMidPointY);
ctx.rotate(angle);
ctx.translate(-rectMidPointX, -rectMidPointY);
ctx.strokeRect(rectX, rectY, rectWidth, rectHeight);
ctx.restore();
// Determine collision
let collision = false;
const findDistance = (fromX, fromY, toX, toY) => {
const a = Math.abs(fromX - toX);
const b = Math.abs(fromY - toY);
return Math.sqrt((a * a) + (b * b));
};
function calculateIntersection() {
// Rotate circle's center point back
const unrotatedCircleX = Math.cos(-angle) * (circleX - rectMidPointX) -
Math.sin(-angle) * (circleY - rectMidPointY) + rectMidPointX;
const unrotatedCircleY = Math.sin(-angle) * (circleX - rectMidPointX) +
Math.cos(-angle) * (circleY - rectMidPointY) + rectMidPointY;
// Closest point in the rectangle to the center of circle rotated backwards(unrotated)
let closestX, closestY;
// Find the unrotated closest x point from center of unrotated circle
if (unrotatedCircleX < rectX)
closestX = rectX;
else if (unrotatedCircleX > rectX + rectWidth)
closestX = rectX + rectWidth;
else
closestX = unrotatedCircleX;
// Find the unrotated closest y point from center of unrotated circle
if (unrotatedCircleY < rectY)
closestY = rectY;
else if (unrotatedCircleY > rectY + rectHeight)
closestY = rectY + rectHeight;
else
closestY = unrotatedCircleY;
const distance = findDistance(unrotatedCircleX, unrotatedCircleY, closestX, closestY);
if (distance < circleRadius)
collision = true; // Collision
else
collision = false;
console.log('collision', collision);
}
<canvas id="canvas" width="400px" height="400px" />

Resources