This question already has answers here:
How to determine if a point is in a 2D triangle? [closed]
(25 answers)
Closed 7 years ago.
For example if I had a triangle with these properties
triangle(100, 300, 200, 200, 300, 300);
or just think of it as
triangle(x1, y1, x2, y2, x3, y3);
How would you write an if statement to check if mousePressed() is within the triangle shape?
As Kevin pointed out, this is an already well documented question and answer.
For fun, there's a hacky workaround to avoid the math using PShape.
There is an undocumented contains() function which checks is an x,y pair is inside all the points of a PShape. That's nice, since this applies for all sort of other shapes, not just triangles. It's a point in polygon test instead of a point in triangle test. There is a catch however. This function isn't documented because it's probably still experimental so there are gotchas:
it works on PATH type only PShapes
PATH type PShapes that aren't loaded (from an SVG file for example) don't render using the shape() function, so you have to manually loop through vertices and draw the shape
Here's an example:
PShape triangle;
void setup(){
size(100,100,P2D);
noStroke();
//create the triangle as PATH type PShape
triangle = new PShape(PShape.PATH);
//add it's vertices
triangle.vertex(50,10);
triangle.vertex(10,90);
triangle.vertex(90,90);
}
void draw(){
background(255);
//check if point is inside triangle
if(triangle.contains(mouseX,mouseY)){
fill(127);
}else{
fill(0);
}
//render triangle accessing the vertices previously set
beginShape(TRIANGLE);
for(int i = 0 ; i < triangle.getVertexCount(); i++){
PVector v = triangle.getVertex(i);
vertex(v.x,v.y);
}
endShape(CLOSE);
}
However, in practice you might find using the math approach simpler (since there are less workarounds to worry about).
Related
I will start off with some background for this problem. It is an extension of several easier problems. Strangely the final extension leads to it being almost impossible to solve.
Given a matrix of integers with 1s, and 0s. 1s representing land and 0s representing ocean count the amount of islands in the matrix which is clusters of 1s separated from each other.
Extend the above problem to donuts. Given the same thing as above only count donuts. That means clusters of 1s with one or more holes of "water" or 0s in it. There can be donuts within donuts.
Extend the above problem to 3D. When you extend the problem above to 3D it becomes hard enough that I will move the question away from "counting donuts" and more towards "is donut?" So in other words, instead of counting clusters of 1s, now you are told that in this 3D grid space there is one and only one cluster of voxels. That cluster is either a donut or it is not a donut. Which means it has a hole (or several holes) going through it or it does not. Write an algorithm to identify this.
Each question is a more challenging extension of the other. With (1.) being the simplest and (3.) being the hardest.
The first 2 questions are quite straight forward. 1. is a classic interview question. (2.) is an extension of that; simply color all separate bodies of water via flood fill with a separate "color" (aka number) and all "islands" touching 2 or more colors is a donut (the hole touches one body of water, and the outside touches a different body of water).
However the 3rd question is challenging. I cannot come up with a way. My coworkers at 2 jobs... nobody could find a way. So I post it here. The isDonut algorithm question.
from typing import List
#3D_space is gaunteed to have one and only one cluster of pixels in it.
def isDonut(3d_space: List[List[List[int]]]) -> bool:
#implement this code
There are many solutions that seem correct but actually fail under specific circumstances. Be careful if you decide to answer.
Edit: For clarity I will define what a voxel donut is:
The above is a voxel donut. A cluster of voxels with a hole going through it. I can only define it in high level english terms. You know it when you see it.
A formal definition of what this is in terms of voxels is the solution to this problem and is described in terms of a programming algorithm. I therefore am unable to describe it, as it's basically my question. Essentially you can think of this question as isomorphic to this one: "what is the formal definition of a voxel donut"?
Edit 2: I'm getting some vague answers and people using advanced math that are hard to understand. Let me put it this way. If you have a straightforward answer you should be able to finish off that python function signature above. You do that the answer is correct, whether or not anyone fully understands your reasoning.
Consider the boundary of your shape: that is, all faces that lie between a filled voxel and an empty one.
Let V be the number of vertices on the boundary, E the number of edges on the boundary, and F the number of faces on the boundary. These are easy to compute; just be careful not to count edges & vertices that belong to multiple boundary faces more than once.
A shape is a donut if and only if (1) the boundary faces are connected, and (2) V-E+F=0.
For more information on this strange and magical second condition, see Euler characteristic.
Edit: This definition does not work. See Rawling's comment below.
Here is an attempt to define a donut, first in a continuous world, through a few observations:
A convex set cannot be a donut. Let S be potential donut, and let conv(S) be the convex hull of S. We define the hole to be H := S \ conv(S). Then S is a donut if H has exactly two disjoint contact surfaces with R^3 \ conv(S). (See below for definitions of "conv()" and "".)
Now, in a discrete voxel world. We can do pretty much the same, except that there are some ambiguities. However, since "donut" is rather informal, they can be resolved according to your personal preferences.
We first need to compute conv(S). There are multiple valid answers here. For example, voxels that partially intersect the continuous conv(S) could be considered part or not part of the discrete convex hull. The construction of H is straightforward, and so are the contact surfaces. The second ambiguity concerns the two disjoint surfaces, specifically what constitutes contiguous voxel faces. A restrictive definition would count 12 neighbors for each voxel face (must have a cube edge in common). But this can be extended to many more if adjacent cube vertices are considered enough.
Note that here I considered that if H is shaped like a Y, then S is not a donut. But this could be up for discussion too.
Disclaimer: not a topologist, my vocabulary may be off. Links to definitions:
Convex hull conv(S): https://en.wikipedia.org/wiki/Convex_hull
S \ conv(S): "set complement" / "Boolean subtraction": https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement / https://en.wikipedia.org/wiki/Constructive_solid_geometry
Edit: Here is an illustration. Yellow: donut. Blue: convex hull. Green: hole. Red: surfaces. Generated with https://evanw.github.io/csg.js/
As you already know #1,#2 then lets focus on #3 (detect if 3D voxel cluster has one ore more holes). After some thinking I revised the original algo a bit:
mark border voxels
so any voxel equal to 1 set to 2 if its neigbors any voxel with 0. After this 0 is empty space, 1 is interior, 2 is surface.
use growth fill to create SDR map of your object
so mark all voxels which are set to 1 to 3 if they neighboring voxel set to 2. Then mark with 4 those which neighbors 3 and so on until no voxel set to 1 is left. This will create something like SDR map (distance to surface).
find and count number of local maximums
for objects without holes there should be just one local max however with holes there would be more of them. In edge case few local max voxels could group to small voxel so count those as one.
Here small C++/OpenGL/VCL example:
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
const int n=40; // voxel map resolution
int map[n][n][n]; // voxel map color
int max_pos[n][3]; // position of local max
int max_cnt=0; // number of local max
int max_dis=0; // number of distinct local max
int pal[32]= // 0xAABBGGRR
{
0x00808080,
0x00707070,
0x00606060,
0x00505050,
0x00404040,
0x00303030,
0x00202020,
0x00101010,
0x00800000,
0x00700000,
0x00600000,
0x00500000,
0x00400000,
0x00300000,
0x00200000,
0x00100000,
0x00008000,
0x00007000,
0x00006000,
0x00005000,
0x00004000,
0x00003000,
0x00002000,
0x00001000,
0x00000080,
0x00000070,
0x00000060,
0x00000050,
0x00000040,
0x00000030,
0x00000020,
0x00000010,
};
//---------------------------------------------------------------------------
void TForm1::draw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
// center the view around map[][][]
float a;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0,0.0,-80.0);
a=-0.4*float(n); glTranslatef(a,a,a);
a=16.0/float(n); glScalef(a,a,a);
// glRotatef( 15.0,1.0,0.0,0.0);
// render map[][][] as cubes (very slow old api version for simplicity)
int x,y,z,i,j;
for (x=0;x<n;x++)
for (y=0;y<n;y++)
for (z=0;z<n;z++)
if (map[x][y][z])
{
glPushMatrix();
glTranslatef(x+x,y+y,z+z);
glColor4ubv((BYTE*)&(pal[map[x][y][z]&31]));
glBegin(GL_QUADS);
for (i=0;i<3*24;i+=3)
{
glNormal3fv(vao_nor+i);
glVertex3fv(vao_pos+i);
}
glEnd();
glPopMatrix();
}
// local max
glDisable(GL_DEPTH_TEST);
glColor4f(0.9,0.2,0.1,1.0);
for (j=0;j<max_cnt;j++)
{
x=max_pos[j][0];
y=max_pos[j][1];
z=max_pos[j][2];
glPushMatrix();
glTranslatef(x+x,y+y,z+z);
glBegin(GL_QUADS);
for (i=0;i<3*24;i+=3)
{
glNormal3fv(vao_nor+i);
glVertex3fv(vao_pos+i);
}
glEnd();
glPopMatrix();
}
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
gl_init(Handle);
// init map[][][]
int x,y,z,xx,yy,zz,c0,c1,c2,e;
int x0=n/2,y0=n/2,z0=n/2,rr0=(n/2)-3; rr0*=rr0; // ball
int x1=n/3,y1=n/2,rr1=(n/5); rr1*=rr1; // cylinder hole
for (x=0;x<n;x++)
for (y=0;y<n;y++)
for (z=0;z<n;z++)
{
// clear map
map[x][y][z]=0;
// ball
xx=x-x0; xx*=xx;
yy=y-y0; yy*=yy;
zz=z-z0; zz*=zz;
if (xx+yy+zz<=rr0) map[x][y][z]=1;
// hole
xx=x-x1; xx*=xx;
yy=y-y1; yy*=yy;
if (xx+yy<=rr1) map[x][y][z]=0;
}
// palette
// for (x=0;(x<n)&&(x<32);x++) map[x][n-1][n-1]=x;
// SDR growth fill
c0=0; // what to neighbor
c1=1; // what to fill
c2=2; // recolor to
for (e=1,c0=0,c1=1,c2=2;e;c0=c2,c2++)
for (e=0,x=1;x<n-1;x++)
for (y=1;y<n-1;y++)
for (z=1;z<n-1;z++)
if (map[x][y][z]==c1)
if ((map[x-1][y][z]==c0)
||(map[x+1][y][z]==c0)
||(map[x][y-1][z]==c0)
||(map[x][y+1][z]==c0)
||(map[x][y][z-1]==c0)
||(map[x][y][z+1]==c0)){ map[x][y][z]=c2; e=1; }
// find local max
max_cnt=0;
max_dis=0;
for (x=1;x<n-1;x++)
for (y=1;y<n-1;y++)
for (z=1;z<n-1;z++)
{
// is local max?
c0=map[x][y][z];
if (map[x-1][y][z]>=c0) continue;
if (map[x+1][y][z]>=c0) continue;
if (map[x][y-1][z]>=c0) continue;
if (map[x][y+1][z]>=c0) continue;
if (map[x][y][z-1]>=c0) continue;
if (map[x][y][z+1]>=c0) continue;
// is connected to another local max?
for (e=0;e<max_cnt;e++)
if (abs(max_pos[e][0]-x)+abs(max_pos[e][1]-y)+abs(max_pos[e][2]-z)==1)
{ e=-1; break; }
if (e>=0) max_dis++;
// add position to list
max_pos[max_cnt][0]=x;
max_pos[max_cnt][1]=y;
max_pos[max_cnt][2]=z;
max_cnt++;
}
Caption=AnsiString().sprintf("local max: %i / %i",max_dis,max_cnt);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
gl_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
gl_resize(ClientWidth,ClientHeight);
draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
draw();
}
//---------------------------------------------------------------------------
Just ignore the VCL and OpenGL stuff (they are not important) and focus on the stuff marked with // SDR growth fill and // find local max comments...
Here preview for ball without and with hole:
The local max are rendered without depth testing in orange color and their count (distinct / all) are printed in window Caption...
I want to make a hole in each of these cubes.
Here is the code:
y=45;
for (i=[1:8]){
z = y*i;
difference(){
rotate([0,0,z]) translate([57,0,-5]) cube(center = true,[5,10,10]);
rotate([90,90,z]) translate([6,0,-60]) cylinder(5,2,2);
}
}
// This is a reference, translate([6,0,-60]) is correct position
rotate([90,90,z]) translate([16,0,-60]) cylinder(5,2,2);
Why
rotate([90,90,z]) translate([6,0,-60]) cylinder(5,2,2);
do not work in a for loop?
When z is setting manually to 45, 90, 135, 180...315 the holes are correct.
So the main loop will position your cuboids rotated around the origin
at an angle which is a multiple of 45 degrees. Inside the loop, you
now want to draw the cuboids, and right after that, relative to the
position of each cuboid, you make a few more transformations (a rotation
and translation) to get the cylinders to pass through the centers of the
cuboids (It also helps if the height of the cylinder is bigger than the
side of the cuboid so you can actually see it passing through):
y=45;
for (i=[1:8]){
z = y*i;
rotate([0,0,z]) translate([57,0,-5])
{
cube(center=true,[5,10,10]);
rotate([0,90,0]) translate([0,0,-5]) cylinder(r=2,h=10,$fn=100);
};
}
Now that you know the positions are correct, you can apply the boolean difference and obtain the hole at the center of each cuboid:
y=45;
for (i=[1:8]){
z = y*i;
rotate([0,0,z]) translate([57,0,-5])
difference() {
cube(center=true,[5,10,10]);
rotate([0,90,0]) translate([0,0,-5]) cylinder(r=2,h=10,$fn=100) ;
}
}
You can find all the code for this here.
So I'm using the Hype library to draw a polygon with a randomized number of edges.
I would like to draw a point at each intersection of these arrays.
The problem being that I don't have the coordinates of these edges, I created an object type HPath inherent to this library in which I specified what size I wanted the polygon to be, its global location and the number of edges.
Is there a method to get the coordinates of whatever is drawn on screen ?
Here is the code if that can help :
import hype.*;
HPath poly1;
void setup() {
size(640,640);
H.init(this).background(#CCCCCC);
poly1 = new HPath();
poly1
.polygon( (int)random(6,12) ) // numEdges
.size(400,400)
.strokeWeight(2)
.stroke(#FFFFFF, 200)
.noFill()
.anchorAt(H.CENTER)
.loc(width/2, height/2)
;
}
void draw() {
H.add(poly1);
H.drawStage();
}
Thanks !
HPath has numVertices() and vertex(index) which should allow you to loop through all the vertices in all HPaths.
Each vertex is in HVertex intance which has a intersectTest() method you could use.
These ingredients you should suffice.
If you're interested in geometry in Processing you should also check out the Geomerative library. If you want resources on learning / coding these sort of intersections yourself check out Paul Bourke's website
I'm doing a scene using openGL (a house). I want to do some collision detection, mainly with the walls in the house.
I have tried the following code:
// a plane is represented with a normal and a position in space
Vector planeNor(0,0,1);
Vector position(0,0,-10);
Plane p(planeNor,position);
Vector vel(0,0,-1);
double lamda; // this is the intersection point
Vector pNormal; // the normal of the intersection
// this method is from Nehe's Lesson 30
coll= p.TestIntersionPlane(vel,Z,lamda,pNormal);
glPushMatrix();
glBegin(GL_QUADS);
if(coll)
glColor3f(1,0,0);
else
glColor3f(1,1,1);
glVertex3d(0,0,-10);
glVertex3d(3,0,-10);
glVertex3d(3,3,-10);
glVertex3d(0,3,-10);
glEnd();
glPopMatrix();
Nehe's method:
#define EPSILON 1.0e-8
#define ZERO EPSILON
bool Plane::TestIntersionPlane(const Vector3 & position,const Vector3 & direction, double& lamda, Vector3 & pNormal)
{
double DotProduct=direction.scalarProduct(normal); // Dot Product Between Plane Normal And Ray Direction
double l2;
// Determine If Ray Parallel To Plane
if ((DotProduct<ZERO)&&(DotProduct>-ZERO))
return false;
l2=(normal.scalarProduct(position))/DotProduct; // Find Distance To Collision Point
if (l2<-ZERO) // Test If Collision Behind Start
return false;
pNormal= normal;
lamda=l2;
return true;
}
Z is initially (0,0,0) and every time I move the camera towards the plane, I reduce its z component by 0.1 (i.e. Z.z-=0.1 ).
I know that the problem is with the vel vector, but I can't figure out what the right value should be. Can anyone please help me?
You're passing "vel" (which I suppose is velocity of the moving thing) as "Position", and Z (which I suppose is position) as "Direction".
Your calculation of "Distance to Collision Point" makes no sense. It doesn't take position of the plane into account at all (or maybe it does, if the variables are misnamed).
You define pNormal, but I can't see any use for it. Is it supposed to mean something else?
It's almost impossible to get something like this working without understanding the math. Try a simpler version of the test, maybe assuming a z=0 plane and +z-axis movement, get that working and then take another look at the general case.
Thank you for your help.
I looked into the code again and I changed the collision detection method into the following:
//startPoint: the ray's starting point.
//EndPoint: the ray's ending point.
//lamda: the intersection point.
bool Plane::TestIntersionPlane(const Vector3& startPoint,const Vector3& Endpoint, double& lamda)
{
double cosAlpha=Endpoint.scalarProduct(normal); // calculates the angle between the plane's normal and the ray vector.
// Determine If Ray Parallel To Plane
if ((cosAlpha<ZERO)&&(cosAlpha>-ZERO))
return false;
// delta D is the plane's distance from the origin minus the ray's distance from the origin.
double deltaD = distance - startPoint.scalarProduct(normal); //distance is a double representing the plane's distance from the origin.
lamda= deltaD/cosAlpha;// distance between the plane and the vector
// if the distance between the ray and the plane is greater than zero then they haven't intersected.
if(lamda > ZERO)
return false;
return true;
}
This seems to work with all planes except when the ray is too far from the plane. For example if the plane is at z=-10 and the ray's starting point is: 0,0,3 and it's ending point is 0,0,2 then this is detected as a collision but when I move the ray to start(0,0,2) and end(0,0,1) it's not detected as a collision.
The math seems correct to me, so I'm not sure how to handle this.
i am trying to extract outline path from given bitmap, i create a fast algorithm (for me) on as3 and that is:
//#v source bitmap's vector data
//#x x to starting extraction
//#y y to stating extraction
//#w extraction width
//#h extraction height
//#mw source bitmap width
//#mh source bitmap height
private function extractRects(v:Vector.<uint>, x:int, y:int,
w:int, h:int, mw:int, mh:int):Array
{
var ary:Array = [], yy:int=y, vStart:int, _xx:int, xx:int;
var lcold:int = 0, lc:int;
//first line to last line
while(yy < h)
{
//first index of current vector
vStart = yy * mw + x;
xx = x;
lc = 0;
//first vector to last on current scan
while(xx < w)
{
/*
if current vector value (color) is
empty (transparent) then
check next
*/
while(xx < w && !v[vStart])
{
vStart++;
xx++;
}
//it is not empty so copy first index
_xx = xx;
//check till value is not empty
while(xx < w && v[vStart])
{
xx++;
vStart++;
}
//create rectangle
var rr:Rectangle = new Rectangle(_xx, yy, (xx-_xx), 1);
//if previous line has the same rectangle index
if(lc < lcold)
{
var oldrr:Rectangle = ary[ary.length - lcold];
//if previous neighbour rect on top
//has same horizontal position then
//resize its height
if(oldrr.left == rr.left && oldrr.width == rr.width)
oldrr.height++;
else
ary.push(rr);
}
else
ary.push(rr);
lc++;
xx++;
}
lcold = lc;
yy++;
}
return ary;
}
With the above method, I extract the region and create shape by drawing rectangles..
Drawing rectangles does not seem to be a good solution because of non-smooth view.
In order to have a smoother view, I must use lines or curves but, using point neighbouring technique is really big headache for me right now.
Could anyone please recommend me any better solution?
as3, c++, c#, vb.net, vb6, delphi, java or similar languages will be fine for answers.
EDIT FOR CLEARIFICATION
I am trying to extract non-transparent pixels' x, y coordinates from a bitmap to draw on different path data. (32 bit ARGB) (creating shape)
For drawing, I could use lineTo, curveTo, moveTo operations.
moveTo(x, y)
lineTo(x, y)
curveTo(cx, cy, ax, ay)
in my code, I thought that I could extract the rectangles of current non-transparent blocks and I could use the same rectangles with moveTo and lineTo operations on further graphic methods
The problem is that this method gives non-smooth look on edges which is neither horizontal nor vertical.
So, the solution is creating a point map on edges, detecting the point neighborhood, using the lineTo operation (because it generates antialiased lines) between neighbour points on rows, or calculating the points placement on nearest circle area and using curveTo method..
Question Could anyone recommend me some algorithms or methods for extracting job?
Thanks in advance
What you're looking for is bitmap/raster image to vector software. To get a good quality result, there are many non-trivial steps that must be performed. There is an open source project called Potrace which does this - click here for a technical description of how it works. If you'd like to try its algorithm in an GUI program, you can use Inkscape with its Trace Bitmap function.