I was trying to expand or shrink the 2d object up to K. Let's say I've a co-ordinate system like { {5,0}, {0,0}, {0,-5}, {5,-5}, {5,-10}, {0,-10} } (yellow marker shape) and I want to offset every position K = 1. The resultant shape would look something like this
As my initial approach was to find the center of the shape and increase each coordinate according to the center, the source is here. But it seems like it only works on a closed symmetric shape like squares and for asymmetric shapes or open shapes, it doesn't work.
My code is given below
void myFun() {
std::vector<std::pair<double, double>> co, CO;
co = { { {5,0}, {0,0}, {0,-5}, {5,-5}, {5,-10}, {0,-10} } };
double x = 0, y = 0;
double n = co.size();
for (auto it : co) {
x += it.first; y += it.second;
}
std::pair<double, double> o = { x / n, y / n };
int K = 1;
for (auto it : co) {
std::pair<double, double> inc = { (it.first - o.first) * K, (it.second - o.second) * K };
CO.push_back({ o.first - inc.first, o.second - inc.second });
}
reverse(CO.begin(), CO.end());
for (auto it: CO) {}
}
The output co-ordinate shape is
How can I improve my approach and solve this issue? Thank you.
I see two ways for you to achieve what you are after:
simply scaling around a point. Take all the coordinates that make up the shape and scale them. To make that happen around a certain point, use the usual approach consisting of those steps: 1. Translate such that (0,0) is the point to scale around, 2, Scale and 3. Translate back. That can easily be done with transform matrices.
Offset each point. That is far more complicated, since it requires you to define an In- / Outside of the shape. Event though this is a description for bezier curves, here you can find a good and illustrative description of the approach. Basically you need to find some kind of normal vector for each Point in the shape and offset it on a fixed amount in that direction.
Maybe there are other way and combinations of the both above which finally snap each point on the grid by rounding.
Related
I am working on a project in which I show a model of a atom with protons, neutrons, and electrons. I want to show the neutrons and protons like a atom model would and so I need some points to put my points around. So I need an function like this
<list of points> getPoints(int numberOfProtonsAndNeutrons)
I have tried iterating over the latitude and longitude, but I couldn't get the intervals to work. That also is not how atom model are visually shown.
So I need an idea of how to go about creating points to put my neutrons and protons on.
Well, assuming you want some neutrons and protons in the center, with some electrons outside in various orbits (the Bohr model), then you need a few things. We can just place all the protons and neutrons on the surface of a sphere with small radius (r=1), and then place the electrons on a sphere with radius increasing depending on the orbital shell the electron is on.
Without doing all the work for you, you need will need an atom class.
It will have a member representing the orbital shell using an index: i=0 could be the innermost shell, with i>0 defining the outer shells with electrons.
You could then compute the radius of the sphere using the index as a member function. Something like
radius() { return i * ShellDist + 1; }
where ShellDist is how far apart the electron shells should be.
You can create a class for the general atom, given its shell and type.
class Atom {
public:
static const double ShellDist = 2; // remember to include a definition for this
Atom(int shell) : shell(shell) {}
virtual void radius() { return shell * ShellDist + 1; }
private:
int shell;
};
class Electron : public Atom {
public:
Electron(int shell) : Atom(shell) {}
};
class Neutron : public Atom {
public:
Neutron() : Atom(0) {}
};
I'm assuming you want all the points on the sphere to be on a plane coincident with the sphere, with the plane going through the origin. To simplify this, we will just choose the circle which goes along the equator of the sphere, and then rotate it using a rotation matrix R.
So the equator circle's equation if z is the vertical axis
Vector3d
circle(double t, double r) { return Vector3d(cos(t) * r, sin(t) * r, 0); }
If z is our vertical axis, then z is always zero here.
Then, choose a rotation matrix R using a vector math library (I'm not sure which one you are using). You can usually create a euler angle representation. Rotate it by some random fixed amount about the x and y axes. This will rotate the circle so we put all the electrons on a random circular orbit around the origin. This is using Eigen:
Transform t = AngleAxis(rand() * 2 * M_PI, Vector3d(0, 1, 0))
* AngleAxis(rand() * 2 * M_PI, Vector3d(1, 0, 0));
Putting it together:
Vector3d createPoint(const Atom& atom, double t) {
auto xform = AngleAxis(rand() * 2 * M_PI, Vector3d(0, 1, 0))
* AngleAxis(rand() * 2 * M_PI, Vector3d(1, 0, 0));
Vector3d electronPoint = xform * circle(t, atom.shellRadius());
return electronPoint;
}
Finally, you can add all the points together for all the atoms in your original array
vector<Vector3d> getPoints() {
vector<shared_ptr<Atom>> atoms;
// construct your atoms depending on the molecule
// eg.
atom.push_back(make_shared<Electron>(1)); // Create an electron on shell 1
atom.push_back(make_shared<Neutron>()); // Create a neutron, which will be shell 0
vector<Vector3d> points;
for (const auto& atom : atoms) {
// instead of rand() you could use some scaling
// of the current time to get an animation with the shells orbiting,
points.push_back(createPoint(atom, rand()));
}
}
Finally, you can then pass this vector of points to irrlicht. You probably want to generate this every frame if you desire an animation.
Good luck! Irrlicht probably has a built-in matrix library which may also help. If you really want to speed things along, you could create custom vertex shaders which do all this math on the GPU, and then simply pass in electrons. This won't be necessary for any known molecules, though :). You also may want a fixed offset per electron. All of this could be done by adding parameters to the Electron and Neutron constructors, to change the start of their orbit.
I am using cocos2d-x 3.8.
I try to create two polygon sprites with the following code.
I know we can detect intersect with BoundingBox but is too rough.
Also, I know we can use Cocos2d-x C++ Physics engine to detect collisions but doesn't it waste a lot of resource of the mobile device? The game I am developing does not need physics engine.
is there a way to detect the intersect of polygon sprites?
Thank you.
auto pinfoTree = AutoPolygon::generatePolygon("Tree.png");
auto treeSprite= Sprite::create(pinfoTree);
treeSprite-> setPosition(width / 4 * 3 - 30 , height / 2 - 200);
this->addChild(treeSprite);
auto pinfoBird = AutoPolygon::generatePolygon("Bird.png");
auto Bird= Sprite::create(pinfoTree);
Bird->setPosition(width / 4 * 3, height / 2);
this->addChild(Bird)
This is a bit more complicated: AutoPolygon gives you a bunch of triangles - the PhysicsBody::createPolygon requires a convex polygon with clockwise winding… so these are 2 different things. The vertex count might even be limited. I think Box2d’s maximum count for 1 polygon is 8.
If you want to try this you’ll have to merge the triangles to form polygons. An option would be to start with one triangle and add more as long as the whole thing stays convex. If you can’t add any more triangles start a new polygon. Add all the polygons as PhysicsShapes to your physics body to form a compound object.
I would propose that you don’t follow this path because
Autopolygon is optimized for rendering - not for best fitting
physics - that is a difference. A polygon traced with Autopolygon will always be bigger than the original sprite - Otherwise you would see rendering artifacts.
You have close to no control over the generated polygons
Tracing the shape in the app will increase your startup time
Triangle meshes and physics outlines are 2 different things
I would try some different approach: Generate the collision shapes offline. This gives you a bunch of advantages:
You can generate and tweak the polygons in a visual editor e.g. by
using PhysicsEditor
Loading the prepares polygons is way faster
You can set additional parameters like mass etc
The solution is battle proven and works out of the box
But if you want to know how polygon intersect work. You can look at this code.
// Calculate the projection of a polygon on an axis
// and returns it as a [min, max] interval
public void ProjectPolygon(Vector axis, Polygon polygon, ref float min, ref float max) {
// To project a point on an axis use the dot product
float dotProduct = axis.DotProduct(polygon.Points[0]);
min = dotProduct;
max = dotProduct;
for (int i = 0; i < polygon.Points.Count; i++) {
flaot d = polygon.Points[i].DotProduct(axis);
if (d < min) {
min = dotProduct;
} else {
if (dotProduct> max) {
max = dotProduct;
}
}
}
}
// Calculate the distance between [minA, maxA] and [minB, maxB]
// The distance will be negative if the intervals overlap
public float IntervalDistance(float minA, float maxA, float minB, float maxB) {
if (minA < minB) {
return minB - maxA;
} else {
return minA - maxB;
}
}
// Check if polygon A is going to collide with polygon B.
public boolean PolygonCollision(Polygon polygonA, Polygon polygonB) {
boolean result = true;
int edgeCountA = polygonA.Edges.Count;
int edgeCountB = polygonB.Edges.Count;
float minIntervalDistance = float.PositiveInfinity;
Vector edge;
// Loop through all the edges of both polygons
for (int edgeIndex = 0; edgeIndex < edgeCountA + edgeCountB; edgeIndex++) {
if (edgeIndex < edgeCountA) {
edge = polygonA.Edges[edgeIndex];
} else {
edge = polygonB.Edges[edgeIndex - edgeCountA];
}
// ===== Find if the polygons are currently intersecting =====
// Find the axis perpendicular to the current edge
Vector axis = new Vector(-edge.Y, edge.X);
axis.Normalize();
// Find the projection of the polygon on the current axis
float minA = 0; float minB = 0; float maxA = 0; float maxB = 0;
ProjectPolygon(axis, polygonA, ref minA, ref maxA);
ProjectPolygon(axis, polygonB, ref minB, ref maxB);
// Check if the polygon projections are currentlty intersecting
if (IntervalDistance(minA, maxA, minB, maxB) > 0)
result = false;
return result;
}
}
The function can be used this way
boolean result = PolygonCollision(polygonA, polygonB);
I once had to program a collision detection algorithm where a ball was to collide with a rotating polygon obstacle. In my case the obstacles where arcs with certain thickness. and where moving around an origin. Basically it was rotating in an orbit. The ball was also rotating around an orbit about the same origin. It can move between orbits. To check the collision I had to just check if the balls angle with respect to the origin was between the lower and upper bound angles of the arc obstacle and check if the ball and the obstacle where in the same orbit.
In other words I used the various constrains and properties of the objects involved in the collision to make it more efficient. So use properties of your objects to cause the collision. Try using a similar approach depending on your objects
I'm making a little app to analyze geometry. In one part of my program, I use an algorithm that has to have a convex object as input. Luckily, all my objects are initially convex, but some are just barely so (see image).
After I apply some transformations, my algorithm fails to work (it produces "infinitely" long polygons, etc), and I think this is because of rounding errors as in the image; the top vertex in the cylinder gets "pushed in" slightly because of rounding errors (very exaggerated in image) and is no longer convex.
So my question is: Does anyone know of a method to "slightly convexify" an object? Here's one method I tried to implement but it didn't seem to work (or I implemented it wrong):
1. Average all vertices together to create a vertex C inside the convex shape.
2. Let d[v] be the distance from C to vertex v.
3. Scale each vertex v from the center C with the scale factor 1 / (1+d[v] * CONVEXIFICATION_FACTOR)
Thanks!! I have CGAL and Boost installed so I can use any of those library functions (and I already do).
You can certainly make the object convex by computing the convex hull of it. But that'll "convexify" anything. If you're sure your input has departed only slightly from being convex, then it shouldn't be a problem.
CGAL appears to have an implementation of 3D Quickhull in it, which would be the first thing to try. See http://doc.cgal.org/latest/Convex_hull_3/ for docs and some example programs. (I'm not sufficiently familiar with CGAL to want to reproduce any examples and claim they're correct.)
In the end I discovered the root of this problem was the fact that the convex hull contained lots of triangles, whereas my input shapes were often cube-shaped, making each quadrilateral region appear as 2 triangles which had extremely similar plane equations, causing some sort of problem in the algorithm I was using.
I solved it by "detriangulating" the polyhedra, using this code. If anyone can spot any improvements or problems, let me know!
#include <algorithm>
#include <cmath>
#include <vector>
#include <CGAL/convex_hull_traits_3.h>
#include <CGAL/convex_hull_3.h>
typedef Kernel::Point_3 Point;
typedef Kernel::Vector_3 Vector;
typedef Kernel::Aff_transformation_3 Transformation;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
struct Plane_from_facet {
Polyhedron::Plane_3 operator()(Polyhedron::Facet& f) {
Polyhedron::Halfedge_handle h = f.halfedge();
return Polyhedron::Plane_3(h->vertex()->point(),
h->next()->vertex()->point(),
h->opposite()->vertex()->point());
}
};
inline static double planeDistance(Plane &p, Plane &q) {
double sc1 = max(abs(p.a()),
max(abs(p.b()),
max(abs(p.c()),
abs(p.d()))));
double sc2 = max(abs(q.a()),
max(abs(q.b()),
max(abs(q.c()),
abs(q.d()))));
Plane r(p.a() * sc2,
p.b() * sc2,
p.c() * sc2,
p.d() * sc2);
Plane s(q.a() * sc1,
q.b() * sc1,
q.c() * sc1,
q.d() * sc1);
return ((r.a() - s.a()) * (r.a() - s.a()) +
(r.b() - s.b()) * (r.b() - s.b()) +
(r.c() - s.c()) * (r.c() - s.c()) +
(r.d() - s.d()) * (r.d() - s.d())) / (sc1 * sc2);
}
static void detriangulatePolyhedron(Polyhedron &poly) {
vector<Polyhedron::Halfedge_handle> toJoin;
for (auto edge = poly.edges_begin(); edge != poly.edges_end(); edge++) {
auto f1 = edge->facet();
auto f2 = edge->opposite()->facet();
if (planeDistance(f1->plane(), f2->plane()) < 1E-5) {
toJoin.push_back(edge);
}
}
for (auto edge = toJoin.begin(); edge != toJoin.end(); edge++) {
poly.join_facet(*edge);
}
}
...
Polyhedron convexHull;
CGAL::convex_hull_3(shape.begin(),
shape.end(),
convexHull);
transform(convexHull.facets_begin(),
convexHull.facets_end(),
convexHull.planes_begin(),
Plane_from_facet());
detriangulatePolyhedron(convexHull);
Plane bounds[convexHull.size_of_facets()];
int boundCount = 0;
for (auto facet = convexHull.facets_begin(); facet != convexHull.facets_end(); facet++) {
bounds[boundCount++] = facet->plane();
}
...
This gave the desired result (after and before):
I'm trying to simulate waves by numerically integrating the wave equation using euler integration (just until I get the kinks worked out, then I'll switch to runge-kutta). I'm using an array of floats as a grid. Then I create a disturbance by changing the value of the grid at one point. Now, instead of radiating in all directions away from this point, the wave only travels in one direction, towards the upper-left, i.e. towards decreasing x and y. So, my question is how do I make the wave radiate out?
Here's my code
void Wave::dudx(float *input,float *output) //calculate du/dx
{
for(int y=0;y<this->height;y++)
{
for(int x=0;x<this->width;x++)
{
output[x+y*this->width]=(this->getPoint((x+1)%this->width,y)-this->getPoint(x,y)); //getPoint returns the value of the grid at (x,y)
}
}
}
void Wave::dudy(float *input,float *output) //calculate du/dy
{
for(int x=0;x<this->width;x++)
{
for(int y=0;y<this->height;y++)
{
output[x+y*this->width]=(this->getPoint(x,(y+1)%this->height)-this->getPoint(x,y));
}
}
}
void Wave::simulate(float dt)
{
float c=6.0f;
//calculate the spatial derivatives
this->dudx(this->points,this->buffer);
this->dudx(this->buffer,this->d2udx2);
this->dudy(this->points,this->buffer);
this->dudy(this->buffer,this->d2udy2);
for(int y=0;y<this->height;y++)
{
for(int x=0;x<this->width;x++)
{
this->points[x+y*this->width]+=c*c*(this->d2udx2[x+y*this->width]+this->d2udy2[x+y*this->width])*dt*dt; //I know that I can calculate c*c and dt*dt once, but I want to make it clear what I'm doing.
}
}
}
Just for the sake of somebody else coming here for the same problem. The usual way to convert the Laplacian to a finite difference expression on a regular grid is:
∆u(x,y) -> idx2*[u(x+1,y) + u(x-1,y) - 2*u(x,y)] +
idy2*[u(x,y+1) + u(x,y-1) - 2*u(x,y)]
where idx2 and idy2 are the inverse squares of the grid spacing in dimension x and y respectively. In the case when the grid spacing in both dimensions is the same, this simplifies to:
∆u(x,y) -> igs2*[u(x+1,y) + u(x-1,y) + u(x,y+1) + u(x,y-1) - 4*u(x,y)]
The multiplicative coefficient can be removed by hiding it inside other coefficients, e.g. c, by changing their units of measurement:
∆u(x,y) -> u(x+1,y) + u(x-1,y) + u(x,y+1) + u(x,y-1) - 4*u(x,y)
By the way, there cannot be 2D spherical waves since spheres are 3D objects. 2D waves are called circular waves.
How can I draw a 2D crescent or moon shape in OpenGL? I have tried using sin and cos like how I did for drawing circles but because a crescent has a "cut" inside it, the sin and cos don't look enough. I couldn't figure out how I could do an intersection between 2 polygons either. So I'm thinking if there a mathematical formula for drawing the crescent?
This isn't mathematically correct, but it may be close enough to meet your needs:
void drawCrescentLine(float step,float scale,float fullness) {
float angle=0.0f;
while (angle<M_PI) {
glVertex2f(scale*sinf(angle),scale*cosf(angle));
angle+=step;
}
while (angle<(2.0f*M_PI)) {
glVertex2f(fullness*scale*sinf(angle),scale*cosf(angle));
angle+=step;
}
glVertex2f(0.0f,scale);
}
or
void drawCrescentTriStrip(float step,float scale,float fullness) {
glVertex2f(0.0f,scale);
float angle=step;
while (angle<M_PI) {
float sinAngle=sinf(angle);
float cosAngle=cosf(angle);
glVertex2f(scale*sinAngle,scale*cosAngle);
glVertex2f(-fullness*scale*sinAngle,scale*cosAngle);
angle+=step;
}
glVertex2f(0.0f,-scale);
}
At fullness=1, it will draw a circle of size scale while at fullness=-0.99f, it will draw a very thin cresent. You could use two different fullness values, rightFullness and leftFullness, and always set one of them to 1.0f so you can change the direction of the crescent.
You can draw two perpendicular ellipses that intersect each other. A crescent is formed with the space that is cut out from one of the eclipses. The intersection can be removed by using a bitwise NAND logical operator when drawing.
glEnable(GL_COLOR_LOGIC_OP);
drawEllipse1();
glLogicOp(GL_NAND);
drawEllipse2();
The long way of doing it is to specify a bunch of vertices that form a skeleton for the shape that you want. You can then 'connect the dots' with GL_LINES to draw your shape. If you want a smoother shape, you can use the vertices as control points for a Bezier/Catmull-Rom spline that would draw a smooth curve joining all your vertices.
You can try this:
Vertex outside [N+1]; // Fill in N with the precision you want
Vertex inside [N+1]; // Fill in N with the precision you want
double neg_size = sqrt (1 + NEG_DIST); // Size of intescting circle.
// NEG_DIST is the distance between their centers
// Greater NEG_DIST => wider crecent
double start_angle = atan (1 / NEG_DIST); // Start angle for the inside edge
double arc = M_PI - (2 * start_angle); // Arc of the inside edge
for (int i = 0; i <= N; i++)
{
// Outside edge
outside [i].x = cos ((M_PI / N) * i) * SIZE;
outside [i].y = sin ((M_PI / N) * i) * SIZE;
// Inside edge
inside [i].x = (cos (start_angle + ((arc / N) * i)) * neg_size) * SIZE;
inside [i].y = (sin (start_angle + ((arc / N) * i)) * neg_size - NEG_DIST) * SIZE;
}
This produces the intersected polys version of a crescent. It will give you an array of coordinates for an inside and outside arc for a crescent. Then you can feed these through your favorite draw method.
NOTE: The endpoints of inside and outside overlap (I did this so that I wouldn't have +/- 1's all over the place). I'm pretty sure a GL program will be fine with it, but if you have a fence post error with this, that may be where it came from