Finding image pixel coordinates (integers) from UV values (floats) of obj file - graphics

I am parsing an obj file which contains the texture coordinates (vt) values. From what I understand, vt values are a mapping into the texture image corresponding to this obj.
Assume, I have image im = 400x300 pixels
and I have a vt value
vt .33345 .8998
The mapping says, in the image, go the coordinate :
imageWidth x .3345 , imageHeight x .8998 and use the value there.
I have loaded the image values in a 2-d array.
The problem is, these mapping coordinates are floating values, how am I suppose to map them to the integer values of the pixel coordinates ? I can always truncate the decimal part, round off etc. But does the standard defines which one of the option is to be done ?

UV coordinates to Pixel coordinates :
pix.x = (uv.x * texture.width) -0.5
pix.y = ((1-uv.y) * texture.height) -0.5
The y axis of uv coordinates is opposite to the Pixel coordinates on an image.
For nearest neighbor interpolation, just round off the pixel coordinates.
For bilinear interpolation, calculate the participation percentage from the four neighbouring pixels and do a weighed average.
When UV coordinates go outside of range, there is a choice on how to handle the "texture wrapping":
Here's some java code for bilinear interpolation with "repeat" texture wrapping:
private static int billinearInterpolation(Point2D uv, BufferedImage texture) {
uv.x = uv.x>0 ? uv.x%1 : 1+(uv.x%1);
uv.y = uv.y>0 ? uv.y%1 : 1+(uv.y%1);
double pixelXCoordinate = uv.x * texture.getWidth() - 0.5;
double pixelYCoordinate = (1-uv.y) * texture.getHeight() - 0.5;
pixelXCoordinate = pixelXCoordinate<0?texture.getWidth()-pixelXCoordinate: pixelXCoordinate;
pixelYCoordinate = pixelYCoordinate<0?texture.getHeight()-pixelYCoordinate : pixelYCoordinate;
int x = (int) Math.floor(pixelXCoordinate);
int y = (int) Math.floor(pixelYCoordinate);
double pX = pixelXCoordinate - x;
double pY = pixelYCoordinate - y;
float[] px = new float[]{(float) (1 - pX), (float) pX};
float[] py = new float[]{(float) (1 - pY), (float) pY};
float red = 0;
float green = 0;
float blue = 0;
float alpha = 0;
for (int i = 0; i < px.length; i++) {
for (int j = 0; j < py.length; j++) {
float p = px[i] * py[j];
if (p != 0) {
int rgb = texture.getRGB((x + i)%texture.getWidth(), (y + j)%texture.getHeight());
alpha += (float) ((rgb >> 24) & 0xFF) * p;
red += (float) ((rgb >> 16) & 0xFF) * p;
green += (float) ((rgb >> 8) & 0xFF) * p;
blue += (float) ((rgb >> 0) & 0xFF) * p;
}
}
}
return (((int) alpha & 0xFF) << 24) |
(((int) red & 0xFF) << 16) |
(((int) green & 0xFF) << 8) |
(((int) blue & 0xFF) << 0);
}

Uv-Coordinates are always in the range [0,1]. This means, you will get the actual pixel coordinates by multiplying them with the image size:
texel_coord = uv_coord * [width, height]
Note, that even here one gets floating point values and there are several ways how to deal with them. The most primitive one is to simply round to the next integer to get the nearest texel. A more sophisticated method would be bilinear filtering.

Related

basic fractal coloring problems

I am trying to get more comfortable with the math behind fractal coloring and understanding the coloring algorithms much better. I am the following paper:
http://jussiharkonen.com/files/on_fractal_coloring_techniques%28lo-res%29.pdf
The paper gives specific parameters to each of the functions, however when I use the same, my results are not quite right. I have no idea what could be going on though.
I am using the iteration count coloring algorithm to start and using the following julia set:
c = 0.5 + 0.25i and p = 2
with the coloring algorithm:
The coloring function simply returns the number of
elements in the truncated orbit divided by 20
And the palette function:
I(u) = k(u − u0),
where k = 2.5 and u0 = 0, was used.
And with a palette being white at 0 and 1, and interpolating to black in-between.
and following this algorithm:
Set z0 to correspond to the position of the pixel in the complex plane.
Calculate the truncated orbit by iterating the formula zn = f(zn−1) starting
from z0 until either
• |zn| > M, or
• n = Nmax,
where Nmax is the maximum number of iterations.
Using the coloring and color index functions, map the resulting truncated
orbit to a color index value.
Determine an RGB color of the pixel by using the palette function
Using this my code looks like the following:
float izoom = pow(1.001, zoom );
vec2 z = focusPoint + (uv * 4.0 - 2.0) * 1.0 / izoom;
vec2 c = vec2(0.5f, 0.25f) ;
const float B = 2.0;
float l;
for( int i=0; i<100; i++ )
{
z = vec2( z.x*z.x - z.y*z.y, 2.0*z.x*z.y ) + c;
if( length(z)>10.0) break;
l++;
}
float ind = basicindex(l);
vec4 col = color(ind);
and have the following index and coloring functions:
float basicindex(float val){
return val / 20.0;
}
vec4 color(float index){
float r = 2.5 * index;
float g = r;
float b = g;
vec3 v = 0.5 - 0.5 * sin(3.14/2.0 + 3.14 * vec3(r, g, b));
return vec4(1.0 - v, 1.0) ;
}
The paper provides the following image:
https://imgur.com/YIZMhaa
While my code produces:
https://imgur.com/OrxdMsN
I get the correct results by using k = 1.0 instead of 2.5, however I would prefer to understand why my results are incorrect. When extending this to the smooth coloring algorithms, my results are still incorrect so I would like to figure this out first.
Let me know if this isn't the correct place for this kind of question and I can move it to the math stack exchange. I wasn't sure which place was more appropriate.
Your image is perfectly implemented for Figure 3.3 in the paper. The other image you posted uses a different routine.
Your figure seems to have that bit of perspective code there at top, but remove that and they should be the same.
If your objection is the color extremes you set that with the "0.5 - 0.5 * ..." part of your code. This makes the darkest black originally 0.5 when in the example image you're trying to duplicate the darkest black should be 1 and the lightest white should be 0.
You're making the whiteness equal to the distance from 0.5
If you ignore the fractal all together you are getting a bunch of values that can be normalized between 0 and 1 and you're coloring those in some particular ways. Clearly the image you are duplicating is linear between 0 and 1 so putting black as 0.5 cannot be correct.
o = {
length : 500,
width : 500,
c : [.5, .25], // c = x + iy will be [x, y]
maxIterate : 100,
canvas : null
}
function point(pos, color){
var c = 255 - Math.round((1 + Math.log(color)/Math.log(o.maxIterate)) * 255);
c = c.toString(16);
if (c.length == 1) c = '0'+c;
o.canvas.fillStyle="#"+c+c+c;
o.canvas.fillRect(pos[0], pos[1], 1, 1);
}
function conversion(x, y, R){
var m = R / o.width;
var x1 = m * (2 * x - o.width);
var y2 = m * (o.width - 2 * y);
return [x1, y2];
}
function f(z, c){
return [z[0]*z[0] - z[1] * z[1] + c[0], 2 * z[0] * z[1] + c[1]];
}
function abs(z){
return Math.sqrt(z[0]*z[0] + z[1]*z[1]);
}
function init(){
var R = (1 + Math.sqrt(1+4*abs(o.c))) / 2,
z, x, y, i;
o.canvas = document.getElementById('a').getContext("2d");
for (x = 0; x < o.width; x++){
for (y = 0; y < o.length; y++){
i = 0;
z = conversion(x, y, R);
while (i < o.maxIterate && abs(z) < R){
z = f(z, o.c);
if (abs(z) > R) break;
i++;
}
if (i) point([x, y], i / o.maxIterate);
}
}
}
init();
<canvas id="a" width="500" height="500"></canvas>
via: http://jsfiddle.net/3fnB6/29/

Simulate virtual camera which preserves color information

I have a virtual scanner that generates a 2.5-D view of a point cloud (i.e. a 2D-projection of a 3D point cloud) depending on camera position. I'm using the vtkCamera.GetProjectionTransformMatrix() to get transformation matrix from world/global to camera coordinates.
However, if the input point cloud has color information for points I would like to preserve it.
Here are the relevant lines:
boost::shared_ptr<pcl::visualization::PCLVisualizer> vis; // camera location, viewpoint and up direction for vis were already defined before
vtkSmartPointer<vtkRendererCollection> rens = vis->getRendererCollection();
vtkSmartPointer<vtkRenderWindow> win = vis->getRenderWindow();
win->SetSize(xres, yres); // xres and yres are predefined resolutions
win->Render();
float dwidth = 2.0f / float(xres),
dheight = 2.0f / float(yres);
float *depth = new float[xres * yres];
win->GetZbufferData(0, 0, xres - 1, yres - 1, &(depth[0]));
vtkRenderer *ren = rens->GetFirstRenderer();
vtkCamera *camera = ren->GetActiveCamera();
vtkSmartPointer<vtkMatrix4x4> projection_transform = camera->GetProjectionTransformMatrix(ren->GetTiledAspectRatio(), 0, 1);
Eigen::Matrix4f mat1;
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
mat1(i, j) = static_cast<float> (projection_transform->Element[i][j]);
mat1 = mat1.inverse().eval();
Now, mat1 is used to transform coordinates to camera-view:
pcl::PointCloud<pcl::PointXYZ>::Ptr &cloud;
int ptr = 0;
for (int y = 0; y < yres; ++y)
{
for (int x = 0; x < xres; ++x, ++ptr)
{
pcl::PointXYZ &pt = (*cloud)[ptr];
if (depth[ptr] == 1.0)
{
pt.x = pt.y = pt.z = std::numeric_limits<float>::quiet_NaN();
continue;
}
Eigen::Vector4f world_coords(dwidth * float(x) - 1.0f,
dheight * float(y) - 1.0f,
depth[ptr],
1.0f);
world_coords = mat1 * world_coords;
float w3 = 1.0f / world_coords[3];
world_coords[0] *= w3;
world_coords[1] *= w3;
world_coords[2] *= w3;
pt.x = static_cast<float> (world_coords[0]);
pt.y = static_cast<float> (world_coords[1]);
pt.z = static_cast<float> (world_coords[2]);
}
}
I want the virtual scanner to return pcl::PointXYZRGB point cloud with color information.
Any help on how to implement this from someone experienced in VTK would save some of my time.
It's possible that I missed a relevant question already asked here - in that case, please point me to it. Thanks.
If I understand correctly that you want to get the color in which the point was rendered into the win RenderWindow, you should be able to get the data from the rendering buffer by calling
float* pixels = win->GetRGBAPixelData(0, 0, xres - 1, yres - 1, 0/1).
This should give you each pixel of the rendering buffer as an array in the format [R0, G0, B0, A0, R1, G1, B1, A1, R2....]. The last parameter which I wrote as 0/1 is whether the data should be taken from front or back opengl buffers. I presume by default double buffering should be on, so then you want to read from back buffer (use '1'), but I am not sure.
Once you have that, you can get the color in your second loop for all pixels that belong to points (depth[ptr] != 1.0) as:
pt.R = pixels[4*ptr];
pt.G = pixels[4*ptr + 1];
pt.B = pixels[4*ptr + 2];
You should call win->ReleaseRGBAPixelData(pixels) once you're done with it.

Code explanation for bitmap conversion

https://stackoverflow.com/a/2574798/159072
public static Bitmap BitmapTo1Bpp(Bitmap img)
{
int w = img.Width;
int h = img.Height;
//
Bitmap bmp = new Bitmap(w, h, PixelFormat.Format1bppIndexed);
BitmapData data = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
Why this addition and division?
byte[] scan = new byte[(w + 7) / 8];
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{////Why this condition check?
if (x % 8 == 0)
//Why divide by 8?
scan[x / 8] = 0;
Color c = img.GetPixel(x, y);
//Why this condition check?
if (c.GetBrightness() >= 0.5)
{
// What is going on here?
scan[x / 8] |= (byte)(0x80 >> (x % 8));
}
}
// Why Martial.Copy() called here?
Marshal.Copy(scan, 0, (IntPtr)((long)data.Scan0 + data.Stride * y), scan.Length);
}
bmp.UnlockBits(data);
return bmp;
}
The code uses some basic bit-hacking techniques, required because it needs to set bits and the minimum storage element you can address in C# is a byte. I intentionally avoided using the BitArray class.
int w = img.Width;
I copy the Width and Height properties of the bitmap into a local variable to speed up the code, the properties are too expensive. Keep in mind that w are the number of pixels across the bitmap, it represents the number of bits in the final image.
byte[] scan = new byte[(w + 7) / 8];
The scan variable stores the pixels in one scan line of the bitmap. The 1bpp format uses 1 bit per pixel so the total number of bytes in a scan line is w / 8. I add 7 to ensure the value is rounded up, necessary because integer division always truncates. w = 1..7 requires 1 byte, w = 8..15 requires 2 bytes, etcetera.
if (x % 8 == 0) scan[x / 8] = 0;
The x % 8 expression represents the bit number, x / 8 is the byte number. This code sets all the pixels to Black when it progresses to the next byte in the scan line. Another way to do it would be re-allocating the byte[] in the outer loop or resetting it back to 0 with a for-loop.
if (c.GetBrightness() >= 0.5)
The pixel should be set to White when the source pixel is bright enough. Otherwise it leaves it at Black. Using Color.Brightness is a simple way to avoid dealing with the human eye's non-linear perception of brightness (luminance ~= 0.299 * red + 0.587 * green + 0.114 * blue).
scan[x / 8] |= (byte)(0x80 >> (x % 8));
Sets a bit to White in the scan line. As noted x % 8 is the bit number, it shifts 0x80 to the right by the bit number, they are stored in reverse order in this pixel format.

How to draw partial-ellipse in CF? (Graphics.DrawArc in full framework)

I hope there will be an easy answer, as often times, something stripped out of Compact Framework has a way of being performed in a seemingly roundabout manner, but works just as well as the full framework (or can be made more efficient).
Simply put, I wish to be able to do a function similar to System.Drawing.Graphics.DrawArc(...) in Compact Framework 2.0.
It is for a UserControl's OnPaint override, where an arc is being drawn inside an ellipse I already filled.
Essentially (close pseudo code, please ignore imperfections in parameters):
FillEllipse(ellipseFillBrush, largeEllipseRegion);
DrawArc(arcPen, innerEllipseRegion, startAngle, endAngle); //not available in CF
I am only drawing arcs in 90 degree spaces, so the bottom right corner of the ellipse's arc, or the top left. If the answer for ANY angle is really roundabout, difficult, or inefficient, while there's an easy solution for just doing just a corner of an ellipse, I'm fine with the latter, though the former would help anyone else who has a similar question.
I use this code, then use FillPolygon or DrawPolygon with the output points:
private Point[] CreateArc(float StartAngle, float SweepAngle, int PointsInArc, int Radius, int xOffset, int yOffset, int LineWidth)
{
if(PointsInArc < 0)
PointsInArc = 0;
if(PointsInArc > 360)
PointsInArc = 360;
Point[] points = new Point[PointsInArc * 2];
int xo;
int yo;
int xi;
int yi;
float degs;
double rads;
for(int p = 0 ; p < PointsInArc ; p++)
{
degs = StartAngle + ((SweepAngle / PointsInArc) * p);
rads = (degs * (Math.PI / 180));
xo = (int)(Radius * Math.Sin(rads));
yo = (int)(Radius * Math.Cos(rads));
xi = (int)((Radius - LineWidth) * Math.Sin(rads));
yi = (int)((Radius - LineWidth) * Math.Cos(rads));
xo += (Radius + xOffset);
yo = Radius - yo + yOffset;
xi += (Radius + xOffset);
yi = Radius - yi + yOffset;
points[p] = new Point(xo, yo);
points[(PointsInArc * 2) - (p + 1)] = new Point(xi, yi);
}
return points;
}
I had this exactly this problem and me and my team solved that creating a extension method for compact framework graphics class;
I hope I could help someone, cuz I spent a lot of work to get this nice solution
Mauricio de Sousa Coelho
Embedded Software Engineer
public static class GraphicsExtension
{
// Implements the native Graphics.DrawArc as an extension
public static void DrawArc(this Graphics g, Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle)
{
//Configures the number of degrees for each line in the arc
int degreesForNewLine = 5;
//Calculates the number of points in the arc based on the degrees for new line configuration
int pointsInArc = Convert.ToInt32(Math.Ceiling(sweepAngle / degreesForNewLine)) + 1;
//Minimum points for an arc is 3
pointsInArc = pointsInArc < 3 ? 3 : pointsInArc;
float centerX = (x + width) / 2;
float centerY = (y + height) / 2;
Point previousPoint = GetEllipsePoint(x, y, width, height, startAngle);
//Floating point precision error occurs here
double angleStep = sweepAngle / pointsInArc;
Point nextPoint;
for (int i = 1; i < pointsInArc; i++)
{
//Increments angle and gets the ellipsis associated to the incremented angle
nextPoint = GetEllipsePoint(x, y, width, height, (float)(startAngle + angleStep * i));
//Connects the two points with a straight line
g.DrawLine(pen, previousPoint.X, previousPoint.Y, nextPoint.X, nextPoint.Y);
previousPoint = nextPoint;
}
//Garantees connection with the last point so that acumulated errors cannot
//cause discontinuities on the drawing
nextPoint = GetEllipsePoint(x, y, width, height, startAngle + sweepAngle);
g.DrawLine(pen, previousPoint.X, previousPoint.Y, nextPoint.X, nextPoint.Y);
}
// Retrieves a point of an ellipse with equation:
private static Point GetEllipsePoint(float x, float y, float width, float height, float angle)
{
return new Point(Convert.ToInt32(((Math.Cos(ToRadians(angle)) * width + 2 * x + width) / 2)), Convert.ToInt32(((Math.Sin(ToRadians(angle)) * height + 2 * y + height) / 2)));
}
// Converts an angle in degrees to the same angle in radians.
private static float ToRadians(float angleInDegrees)
{
return (float)(angleInDegrees * Math.PI / 180);
}
}
Following up from #ctacke's response, which created an arc-shaped polygon for a circle (height == width), I edited it further and created a function for creating a Point array for a curved line, as opposed to a polygon, and for any ellipse.
Note: StartAngle here is NOON position, 90 degrees is the 3 o'clock position, so StartAngle=0 and SweepAngle=90 makes an arc from noon to 3 o'clock position.
The original DrawArc method has the 3 o'clock as 0 degrees, and 90 degrees is the 6 o'clock position. Just a note in replacing DrawArc with CreateArc followed by DrawLines with the resulting Point[] array.
I'd play with this further to change that, but why break something that's working?
private Point[] CreateArc(float StartAngle, float SweepAngle, int PointsInArc, int ellipseWidth, int ellipseHeight, int xOffset, int yOffset)
{
if (PointsInArc < 0)
PointsInArc = 0;
if (PointsInArc > 360)
PointsInArc = 360;
Point[] points = new Point[PointsInArc];
int xo;
int yo;
float degs;
double rads;
//could have WidthRadius and HeightRadius be parameters, but easier
// for maintenance to have the diameters sent in instead, matching closer
// to DrawEllipse and similar methods
double radiusW = (double)ellipseWidth / 2.0;
double radiusH = (double)ellipseHeight / 2.0;
for (int p = 0; p < PointsInArc; p++)
{
degs = StartAngle + ((SweepAngle / PointsInArc) * p);
rads = (degs * (Math.PI / 180));
xo = (int)Math.Round(radiusW * Math.Sin(rads), 0);
yo = (int)Math.Round(radiusH * Math.Cos(rads), 0);
xo += (int)Math.Round(radiusW, 0) + xOffset;
yo = (int)Math.Round(radiusH, 0) - yo + yOffset;
points[p] = new Point(xo, yo);
}
return points;
}

How to test if a line segment intersects an axis-aligned rectange in 2D?

How to test if a line segment intersects an axis-aligned rectange in 2D? The segment is defined with its two ends: p1, p2. The rectangle is defined with top-left and bottom-right points.
The original poster wanted to DETECT an intersection between a line segment and a polygon. There was no need to LOCATE the intersection, if there is one. If that's how you meant it, you can do less work than Liang-Barsky or Cohen-Sutherland:
Let the segment endpoints be p1=(x1 y1) and p2=(x2 y2).
Let the rectangle's corners be (xBL yBL) and (xTR yTR).
Then all you have to do is
A. Check if all four corners of the rectangle are on the same side of the line.
The implicit equation for a line through p1 and p2 is:
F(x y) = (y2-y1)*x + (x1-x2)*y + (x2*y1-x1*y2)
If F(x y) = 0, (x y) is ON the line.
If F(x y) > 0, (x y) is "above" the line.
If F(x y) < 0, (x y) is "below" the line.
Substitute all four corners into F(x y). If they're all negative or all positive, there is no intersection. If some are positive and some negative, go to step B.
B. Project the endpoint onto the x axis, and check if the segment's shadow intersects the polygon's shadow. Repeat on the y axis:
If (x1 > xTR and x2 > xTR), no intersection (line is to right of rectangle).
If (x1 < xBL and x2 < xBL), no intersection (line is to left of rectangle).
If (y1 > yTR and y2 > yTR), no intersection (line is above rectangle).
If (y1 < yBL and y2 < yBL), no intersection (line is below rectangle).
else, there is an intersection. Do Cohen-Sutherland or whatever code was mentioned in the other answers to your question.
You can, of course, do B first, then A.
Alejo
Wrote quite simple and working solution:
bool SegmentIntersectRectangle(double a_rectangleMinX,
double a_rectangleMinY,
double a_rectangleMaxX,
double a_rectangleMaxY,
double a_p1x,
double a_p1y,
double a_p2x,
double a_p2y)
{
// Find min and max X for the segment
double minX = a_p1x;
double maxX = a_p2x;
if(a_p1x > a_p2x)
{
minX = a_p2x;
maxX = a_p1x;
}
// Find the intersection of the segment's and rectangle's x-projections
if(maxX > a_rectangleMaxX)
{
maxX = a_rectangleMaxX;
}
if(minX < a_rectangleMinX)
{
minX = a_rectangleMinX;
}
if(minX > maxX) // If their projections do not intersect return false
{
return false;
}
// Find corresponding min and max Y for min and max X we found before
double minY = a_p1y;
double maxY = a_p2y;
double dx = a_p2x - a_p1x;
if(Math::Abs(dx) > 0.0000001)
{
double a = (a_p2y - a_p1y) / dx;
double b = a_p1y - a * a_p1x;
minY = a * minX + b;
maxY = a * maxX + b;
}
if(minY > maxY)
{
double tmp = maxY;
maxY = minY;
minY = tmp;
}
// Find the intersection of the segment's and rectangle's y-projections
if(maxY > a_rectangleMaxY)
{
maxY = a_rectangleMaxY;
}
if(minY < a_rectangleMinY)
{
minY = a_rectangleMinY;
}
if(minY > maxY) // If Y-projections do not intersect return false
{
return false;
}
return true;
}
Since your rectangle is aligned, Liang-Barsky might be a good solution. It is faster than Cohen-Sutherland, if speed is significant here.
Siggraph explanation
Another good description
And of course, Wikipedia
You could also create a rectangle out of the segment and test if the other rectangle collides with it, since it is just a series of comparisons. From pygame source:
def _rect_collide(a, b):
return a.x + a.w > b.x and b.x + b.w > a.x and \
a.y + a.h > b.y and b.y + b.h > a.y
Use the Cohen-Sutherland algorithm.
It's used for clipping but can be slightly tweaked for this task. It divides 2D space up into a tic-tac-toe board with your rectangle as the "center square".
then it checks to see which of the nine regions each of your line's two points are in.
If both points are left, right, top, or bottom, you trivially reject.
If either point is inside, you trivially accept.
In the rare remaining cases you can do the math to intersect with whichever sides of the rectangle are possible to intersect with, based on which regions they're in.
Or just use/copy the code already in the Java method
java.awt.geom.Rectangle2D.intersectsLine(double x1, double y1, double x2, double y2)
Here is the method after being converted to static for convenience:
/**
* Code copied from {#link java.awt.geom.Rectangle2D#intersectsLine(double, double, double, double)}
*/
public class RectangleLineIntersectTest {
private static final int OUT_LEFT = 1;
private static final int OUT_TOP = 2;
private static final int OUT_RIGHT = 4;
private static final int OUT_BOTTOM = 8;
private static int outcode(double pX, double pY, double rectX, double rectY, double rectWidth, double rectHeight) {
int out = 0;
if (rectWidth <= 0) {
out |= OUT_LEFT | OUT_RIGHT;
} else if (pX < rectX) {
out |= OUT_LEFT;
} else if (pX > rectX + rectWidth) {
out |= OUT_RIGHT;
}
if (rectHeight <= 0) {
out |= OUT_TOP | OUT_BOTTOM;
} else if (pY < rectY) {
out |= OUT_TOP;
} else if (pY > rectY + rectHeight) {
out |= OUT_BOTTOM;
}
return out;
}
public static boolean intersectsLine(double lineX1, double lineY1, double lineX2, double lineY2, double rectX, double rectY, double rectWidth, double rectHeight) {
int out1, out2;
if ((out2 = outcode(lineX2, lineY2, rectX, rectY, rectWidth, rectHeight)) == 0) {
return true;
}
while ((out1 = outcode(lineX1, lineY1, rectX, rectY, rectWidth, rectHeight)) != 0) {
if ((out1 & out2) != 0) {
return false;
}
if ((out1 & (OUT_LEFT | OUT_RIGHT)) != 0) {
double x = rectX;
if ((out1 & OUT_RIGHT) != 0) {
x += rectWidth;
}
lineY1 = lineY1 + (x - lineX1) * (lineY2 - lineY1) / (lineX2 - lineX1);
lineX1 = x;
} else {
double y = rectY;
if ((out1 & OUT_BOTTOM) != 0) {
y += rectHeight;
}
lineX1 = lineX1 + (y - lineY1) * (lineX2 - lineX1) / (lineY2 - lineY1);
lineY1 = y;
}
}
return true;
}
}
A quick Google search popped up a page with C++ code for testing the intersection.
Basically it tests the intersection between the line, and every border or the rectangle.
Rectangle and line intersection code
Here's a javascript version of #metamal's answer
var isRectangleIntersectedByLine = function (
a_rectangleMinX,
a_rectangleMinY,
a_rectangleMaxX,
a_rectangleMaxY,
a_p1x,
a_p1y,
a_p2x,
a_p2y) {
// Find min and max X for the segment
var minX = a_p1x
var maxX = a_p2x
if (a_p1x > a_p2x) {
minX = a_p2x
maxX = a_p1x
}
// Find the intersection of the segment's and rectangle's x-projections
if (maxX > a_rectangleMaxX)
maxX = a_rectangleMaxX
if (minX < a_rectangleMinX)
minX = a_rectangleMinX
// If their projections do not intersect return false
if (minX > maxX)
return false
// Find corresponding min and max Y for min and max X we found before
var minY = a_p1y
var maxY = a_p2y
var dx = a_p2x - a_p1x
if (Math.abs(dx) > 0.0000001) {
var a = (a_p2y - a_p1y) / dx
var b = a_p1y - a * a_p1x
minY = a * minX + b
maxY = a * maxX + b
}
if (minY > maxY) {
var tmp = maxY
maxY = minY
minY = tmp
}
// Find the intersection of the segment's and rectangle's y-projections
if(maxY > a_rectangleMaxY)
maxY = a_rectangleMaxY
if (minY < a_rectangleMinY)
minY = a_rectangleMinY
// If Y-projections do not intersect return false
if(minY > maxY)
return false
return true
}
I did a little napkin solution..
Next find m and c and hence the equation y = mx + c
y = (Point2.Y - Point1.Y) / (Point2.X - Point1.X)
Substitute P1 co-ordinates to now find c
Now for a rectangle vertex, put the X value in the line equation, get the Y value and see if the Y value lies in the rectangle bounds shown below
(you can find the constant values X1, X2, Y1, Y2 for the rectangle such that)
X1 <= x <= X2 &
Y1 <= y <= Y2
If the Y value satisfies the above condition and lies between (Point1.Y, Point2.Y) - we have an intersection.
Try every vertex if this one fails to make the cut.
I was looking at a similar problem and here's what I came up with. I was first comparing the edges and realized something. If the midpoint of an edge that fell within the opposite axis of the first box is within half the length of that edge of the outer points on the first in the same axis, then there is an intersection of that side somewhere.
But that was thinking 1 dimensionally and required looking at each side of the second box to figure out.
It suddenly occurred to me that if you find the 'midpoint' of the second box and compare the coordinates of the midpoint to see if they fall within 1/2 length of a side (of the second box) of the outer dimensions of the first, then there is an intersection somewhere.
i.e. box 1 is bounded by x1,y1 to x2,y2
box 2 is bounded by a1,b1 to a2,b2
the width and height of box 2 is:
w2 = a2 - a1 (half of that is w2/2)
h2 = b2 - b1 (half of that is h2/2)
the midpoints of box 2 are:
am = a1 + w2/2
bm = b1 + h2/2
So now you just check if
(x1 - w2/2) < am < (x2 + w2/2) and (y1 - h2/2) < bm < (y2 + h2/2)
then the two overlap somewhere.
If you want to check also for edges intersecting to count as 'overlap' then
change the < to <=
Of course you could just as easily compare the other way around (checking midpoints of box1 to be within 1/2 length of the outer dimenions of box 2)
And even more simplification - shift the midpoint by your half lengths and it's identical to the origin point of that box. Which means you can now check just that point for falling within your bounding range and by shifting the plain up and to the left, the lower corner is now the lower corner of the first box. Much less math:
(x1 - w2) < a1 < x2
&&
(y1 - h2) < b1 < y2
[overlap exists]
or non-substituted:
( (x1-(a2-a1)) < a1 < x2 ) && ( (y1-(b2-b1)) < b1 < y2 ) [overlap exists]
( (x1-(a2-a1)) <= a1 <= x2 ) && ( (y1-(b2-b1)) <= b1 <= y2 ) [overlap or intersect exists]
coding example in PHP (I'm using an object model that has methods for things like getLeft(), getRight(), getTop(), getBottom() to get the outer coordinates of a polygon and also has a getWidth() and getHeight() - depending on what parameters were fed it, it will calculate and cache the unknowns - i.e. I can create a polygon with x1,y1 and ... w,h or x2,y2 and it can calculate the others)
I use 'n' to designate the 'new' item being checked for overlap ($nItem is an instance of my polygon object) - the items to be tested again [this is a bin/sort knapsack program] are in an array consisting of more instances of the (same) polygon object.
public function checkForOverlaps(BinPack_Polygon $nItem) {
// grab some local variables for the stuff re-used over and over in loop
$nX = $nItem->getLeft();
$nY = $nItem->getTop();
$nW = $nItem->getWidth();
$nH = $nItem->getHeight();
// loop through the stored polygons checking for overlaps
foreach($this->packed as $_i => $pI) {
if(((($pI->getLeft() - $nW) < $nX) && ($nX < $pI->getRight())) &&
((($pI->getTop() - $nH) < $nY) && ($nY < $pI->getBottom()))) {
return false;
}
}
return true;
}
Some sample code for my solution (in php):
// returns 'true' on overlap checking against an array of similar objects in $this->packed
public function checkForOverlaps(BinPack_Polygon $nItem) {
$nX = $nItem->getLeft();
$nY = $nItem->getTop();
$nW = $nItem->getWidth();
$nH = $nItem->getHeight();
// loop through the stored polygons checking for overlaps
foreach($this->packed as $_i => $pI) {
if(((($pI->getLeft() - $nW) < $nX) && ($nX < $pI->getRight())) && ((($pI->getTop() - $nH) < $nY) && ($nY < $pI->getBottom()))) {
return true;
}
}
return false;
}

Resources