How to calculate how many rectangles fit in a polygon? - javascript

I'm creating an algorithm using JavaScript + Canvas, the user can draw a polygon, after it I want to calculate how many rectangles (suppose I know the dimensions) fit inside this polygon?
For example, I have 3 examples of a yellow polygon (rectangle, triangle, and a complex), I want to know how many rectangles fit inside it.

If rectangles can be cut
The area of a rectangle is width * height.
The area of any polygon with straight edges can be found from the set of points that define the vertices using the function below. Half the sum of the cross products of vertices defining each edge.
const polyArea = points => {
var i = 0, area = 0, len = points.length;
while (i < len) {
const p1 = points[i++];
const p2 = points[i % len];
area += p1.x * p2.y - p1.y * p2.x;
}
return Math.abs(0.5 * area);
}
// example
const triangleArea = polyArea([{x:100, y:0}, {x: 200, y: 200}, {x: 0, y: 200}]);
The max number of rectangles that can fit in a polygon is the polygonArea / rectangleArea.
If rectangles can not be cut
However the actual number of rectangles will be some value from 0 to the number calculated above.
There is no simple solution to the problem apart from the brute force approach of placing rectangles in different patterns to see which pattern is the best fit. For each polygon there is an infinite number of patterns that can be used, thus any solution will be just the best of many guess if you want id done before lunch.

Related

Convert 2D shape into 3D in d3.js and adjust height according to the value in ANGULAR

I am using d3.js v6 to create a 3D graph of the below 2D chart representation. This circle has multiple squares in it and each square has been assigned a color based on the value. The bigger the value, more darker the square.
Now I want to convert this in 3D shape where only the height of a particular square increases when the value gets high, so the result would be somehow similar to the image below. The base would be circular but the height of each value would go up based on the value
I am trying to achieve this in angular, if anyone could please help me out. Here is the Stackblitz Link
I made the one as you requested.
source code on github
here's working demo: https://stackoverflow-angular-3d-chart.surge.sh/
This involved several intricate steps.
I couldn't go any deeper from this answer because every part that I mentioned here could be hours worth tutorial. These are what I've felt interesting when I was working on it.
Used Stacks
EDIT: the stackblitz code is now outdated. I've used the most recent version for each package.
Three.js r143
D3.js v7.6.1
Angular.js v14
Getting Circle Grid
experiment note on ObservableHQ: https://observablehq.com/#rabelais/circle-inside-grids
First I've experimented on SVG with D3.js to get proper circle grid.
It seemed daunting but turned out very simple. I've slightly modified Midpoint circle algorithm to fill box grids in circular shape. It is little different from filling grids in 3d space; 2d space has top left corner as beginning of everything. In 3d space, everything starts from center.
const midPointX = gridWidth / 2;
const midPointY = gridHeight / 2;
const { midPointX, midPointY, radius } = config;
const getCollision = ({ x, y }) => {
return (midPointX - x) ** 2 + (midPointY - y) ** 2 - radius ** 2 > 0;
}
Calculating Gaps
d3's scale band supports automatic calculation of gaps and content size in responsive environment.
const scaleBandX = d3
.scaleBand()
.domain(d3.range(0, config.gridWidth))
.range([config.margin, config.svgWidth - config.margin * 2])
.paddingInner(0.2);
const scaleBandY = d3
.scaleBand()
.domain(d3.range(0, config.gridHeight))
.range([config.margin, config.svgHeight - config.margin * 2])
.paddingInner(0.2);
scaleBandX.bandwidth(); // width of box in 2d space
scaleBandY.bandwidth(); // height of box in 2d space
scaleBandX(boxIndex); // x position of box in 2d space with gap
scaleBandY(boxIndex); // y position of box in 2d space with gap
as D3 assumes vector calculation as normal, it was pretty easy to apply the very same method in 3D.
Expressing on 3D space
I've used Three.js to express everything in 3D. The app is running on Angular per request but it does not matter which frontend framework is used.
Everything about expressing 2d bar chart on 3d is very trivial. However, the dimension is different from 2d; the positions have to be swapped.
// code to make a single bar mesh
makeBar(d: typeof gridData[0]) {
// length and height is swapped. because camera is looking from 90 degree angle by default.
const geo = new T.BoxGeometry(d.w, d.l, d.h, 32, 32);
const mat = new T.MeshPhysicalMaterial({ color: 'red' });
const mesh = new T.Mesh(geo, mat);
mesh.position.x = d.x;
// z and y is also swapped. because of the same reason.
mesh.position.z = d.y;
mesh.position.y = d.z;
return mesh;
}
then each element is assigned as 3d Group, to make them centered altogether.
EDIT: color scheme was missing. it is now added.

detect collision between two circles and sliding them on each other

I'm trying to detect collision between two circles like this:
var circle1 = {radius: 20, x: 5, y: 5}; //moving
var circle2 = {radius: 12, x: 10, y: 5}; //not moving
var dx = circle1.x - circle2.x;
var dy = circle1.y - circle2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < circle1.radius + circle2.radius) {
// collision detected
}else{
circle1.x += 1 * Math.cos(circle1.angle);
circle1.y += 1 * Math.sin(circle1.angle);
}
Now when collision is detected I want to slide the circle1 from on the circle2 (circle1 is moving) like this:
--circle1---------------------------------circle2-------------------------
I could do this by updating the angle of circle1 and Moving it toward the new angle when collision is detected.
Now My question is that how can I detect whether to update/increase the angle or update/decrease the angle based on which part of circle2 circle1 is colliding with ?? (circle one comes from all angles)
I would appreciate any help
This will depend a bit on how you are using these circles, and how many will ever exist in a single system, but if you are trying to simulate the effect of two bodies colliding under gravity where one roles around to the edge then falls off (or similar under-thrust scenario), then you should apply a constant acceleration or velocity to the moving object and after you compute it's movement phase, you do a displacement phase where you take the angle to the object you are colliding with and move it back far enough in that direction to reach circle1.radius + circle2.radius.
[edit] To get that redirection after falling though (not sure if you intended this or if it's just your sketch), there is probably going to be another force at play. Most likely it will involve a "stickiness" applied between the bodies. Basically, on a collision, you need to make sure that on the next movement cycle, you apply Normal Movement, then movement towards the other body, then the repulsion to make sure they don't overlap. This way it will stick to the big circle until gravity pulls way at enough of a direct angle to break the connection.
[edit2] If you want to make this smoother and achieve a natural curve as you fall away you can use an acceleration under friction formula. So, instead of this:
circle1.x += 1 * Math.cos(circle1.angle);
circle1.y += 1 * Math.sin(circle1.angle);
You want to create velocity properties for your object that are acted on by acceleration and friction until they balance out to a fixed terminal velocity. Think:
// constants - adjust these to get the speed and smoothness you desire
var accelerationX = 1;
var accelerationY = 0;
var friction = 0.8;
// part of physics loop
circle1.velX += (accelerationX * Math.cos(circle1.angle)) - (friction * circle1.velX);
circle1.velY += (accelerationY * Math.sin(circle1.angle)) - (friction * circle1.velX);
circle1.x += circle1.velX;
circle1.y += circle1.velY;
This way, when things hit they will slow down (or stop), then speed back up when they start moving again. The acceleration as it gets back up to speed will achieve a more natural arc as it falls away.
You could get the tangent of the point of contact between both circles, which would indicate you how much to change your angle compared to the destination point (or any horizontal plane).

d3.js -- line connecting two transformed svg elements

I created an array of small circles (dots) laid on a circumference of a larger circle in a loop, with each iteration I rotate the same dot with the same cx and cy by a different angle using transform=rotate(i*angle,0,0).
Now I want to connect a line between two of the dots, but since each dot has the same cx and cy, my naive way of passing in the cx and cy of the two dots as coordinates doesn't seem to work.
Strangely on JSFiddle, half of the line is shown, even though the line's x0 == x1 and y0 == y1: https://jsfiddle.net/8wn30vqn/1/
What is a good way of obtaining the coordinates of a transformed svg element so I can pass them into the line? Or is there some other way I can connect two elements with a line?
The good way would be to take transformation matrix from each of your small circles and apply it to the same point to get new point (and then connect these new points).
var svgNode = d3circle.node();
var matrix = svgNode.transform.baseVal.consolidate().matrix;
var pt = svgNode.ownerSVGElement.createSVGPoint();
pt.x = 0;
pt.y = 0;
var transformedPoint = pt.matrixTransform(matrix);
... use transformedPoint to create line

Algorithm for moving an object horizontally in javascript

I am currently working on a game using javascript and processing.js and I am having trouble trying to figure out how to move stuff diagonally. In this game, there is an object in the center that shoots other objects around it. Now I have no problem moving the bullet only vertically or only horizontally, however I am having difficulty implementing a diagonal motion for the bullet algorithm.
In terms of attempts, I tried putting on my math thinking cap and used the y=mx+b formula for motion along a straight line, but this is what my code ends up looking like:
ellipse(shuriken.xPos, shuriken.yPos, shuriken.width, shuriken.height); //this is what I want to move diagonally
if(abs(shuriken.slope) > 0.65) {
if(shuriken.targetY < shuriken.OrigYPos) {
shuriken.yPos -= 4;
} else {
shuriken.yPos += 4;
}
shuriken.xPos = (shuriken.yPos - shuriken.intercept)/shuriken.slope;
} else {
if(shuriken.targetX < shuriken.OrigXPos) {
shuriken.xPos -= 4;
} else {
shuriken.xPos += 4;
}
shuriken.yPos = shuriken.slope * shuriken.xPos + shuriken.intercept;
}
The above code is very bad and hacky as the speed varies with the slope of the line.
I tried implementing a trigonometry relationship but still in vain.
Any help/advice will be greatly appreciated!
Think of it this way: you want the shuriken to move s pixels. If the motion is horizontal, it should move s pixels horizontally; if vertical, s pixels vertically. However, if it's anything else, it will be a combination of pixels horizontally/vertically. What's the correct combination? Well, what shape do you get if you project s distance in any direction from a given point? That's right, a circle with radius s. Let's represent the direction in terms of an angle, a. So we have this picture:
How do we get the x and the y? If you notice, we have a triangle. If you recall your trigonometry, this is precisely what the sine, cosine, and tangent functions are for. I learned their definitions via the mnemonic SOHCAHTOA. That is: Sin (a) = Opposite/Hypotenuse, Cos(a) = Adjacent/Hypotenuse, Tan(a) = Opposite/Adjacent. In this case, opposite of angle a is y, and adjacent of angle a is x. Thus we have:
cos(a) = x / s
sin(a) = y / s
Solving for x and y:
x = s * cos(a)
y = s * sin(a)
So, given the angle a, and that you want to move your shuriken s pixels, you want to move it s * cos(a) horizontally and s * sin(a) vertically.
Just be sure you pass a in radians, not degrees, to javascript's Math.sin and Math.cos functions:
radians = degrees * pi / 180.0
This may be why your trigonometric solution didn't work as this has bitten me a bunch in the past.
If you know the angle and speed you are trying to move at, you can treat it as a polar coordinate, then convert to cartesian coordinates to get an x,y vector you would need to move the object by to go in that direction and speed.
If you don't know the angle, you could also come up with the vector by taking the difference in X and difference in Y (this I know you can do as you are able to calculate the slope between the 2 points). Then take the resulting vector and divide by the length of the vector to get a unit vector, which you can then scale to your speed to get a final vector in which you can move your object by.
(This is what probably what kennypu means by sticking with vectors?)

Translating an element with canvas

I'm trying to learn canvas by implementing a pie chart. I've managed to parse my data, draw the slices, and calculate the center of each arc, as noted by the black circles. But now I'm trying to draw one of the slices as though it had been "slid out". Not animate it (yet), just simply draw the slice as though it had been slid out.
I thought the easiest way would be to first calculate the point at which the new corner of the slice should be (free-hand drawn with the red X), translate there, draw my slice, then translate the origin back. I thought I could calculate this easily, since I know the center of the pie chart, and the point of the center of the arc (connected with a free-hand black line on the beige slice). But after asking this question, it seems this will involve solving a system of equations, one of which is second order. That's easy with a pen and paper, dauntingly hard in JavaScript.
Is there a simpler approach? Should I take a step back and realize that doing this is really the same as doing XYZ?
I know I haven't provided any code, but I'm just looking for ideas / pseudocode. (jQuery is tagged in the off chance there's a plugin will somehow help in this endeavor)
Getting the x and y of the translation is easy enough.
// cx and cy are the coordinates of the centre of your pie
// px and py are the coordinates of the black circle on your diagram
// off is the amount (range 0-1) by which to offset the arc
// adjust off as needed.
// rx and ry will be the amount to translate by
var dx = px-cx, dy = py-cy,
angle = Math.atan2(dy,dx),
dist = Math.sqrt(dx*dx+dy*dy);
rx = Math.cos(angle)*off*dist;
ry = Math.sin(angle)*off*dist;
Plug that into the code Simon Sarris gave you and you're done. I'd suggest an off value of 0.25.
Merely translating an element on a canvas is very easy and there shouldn't be any tricky equations here. In the most basic sense it is:
ctx.save();
ctx.translate(x, y);
// Draw the things you want offset by x, y
ctx.restore();
Here's a rudimentary example of a square pie and the same pie with one of the four "slices" translated:
http://jsfiddle.net/XqwY2/
To make the pie piece "slide out" the only thing you need to calculate is how far you want it to be. In my simple example the blue block is slid out 10, -10.
If you are wondering merely how to get the X and Y you want in the first place, well, that's not quite a javascript/canvas question. For points on a line given a distance this question: Finding points on a line with a given distance seems the most clear
Edit, here you are (from comments):
// Center point of pie
var x1 = 100;
var y1 = 100;
// End of pie slice (your black dot)
var x2 = 200;
var y2 = 0;
// The distance you want
var distance = 3;
var vx = x2 - x1; // x vector
var vy = y2 - y1; // y vector
var mag = Math.sqrt(vx*vx + vy*vy); // length
vx = mag/vx;
vy = mag/vy;
// The red X location that you want:
var px = x1 + vx * ( distance);
var py = y1 + vy * ( distance);
This would give you a px,py of (104.24, 95.76) for my made-up inputs.

Categories

Resources