Find polygons from set of lines - objective-c

I'm working in 2D.
My user can draw some lines & line-segments, which I store in a custom object class, but basically in startX-Y & endX-Y.
Now I want to find, actually only the polygon where the ball is inside, but after reading and researching about some algoirthms etc. I think I have to find all polygons and serach after that the right one.
Is there anyone with some example code, c#, java objective-c !?
I tried several times with some pseudo-code explanations, I don't get it.

Overview
There are a number of different interesting questions at play here:
1. Given you have a set of lines on the screen, and the user places their finger from an arbitrary point and drags it to an arbitrary end point, we need to form a new line which does not cross over lines and where the start and end point actually lie on existing lines or on borders.
2. The next question is how do we maintain an active set of "relevant" line segments which form the convex hull which the ball resides in.
3. Finally given we have an active set of line segments how do we find the area of this shape.
Now It seems you've already answered part 1. So we focus on parts 2 and 3.
For part 2, we will also be interested in asking:
4. If we have a convex hull and a point, how do we determine if that point is in the hull.
We refer to here for the solution to 4 Find if a point is inside a convex hull for a set of points without computing the hull itself
The full implementation can be done efficiently if you are careful with the data structures you are using. This is left as a simple exercise. Let me know if I have done something incorrect here or you do not understand something. I look forward to playing your game when its ready!
Solution to Part 3
In fact 3 is easy from 2, since if we know the active set of line segments containing the ball (this is a list of pairs of tuples ((x_1start,y_1start), (x_1end, y1_end))), there are two ways to compute the area. The first is to do a straightforward algorithm to compute the area of the convex hull formed by all start and end points in this list of tuples. Look up more sophisticated algorithms for area, but if you cannot find one, note that the hull with n sides has n-2 non-overlapping triangles, and the area of triangles is easy to compute (http://www.mathopenref.com/polygontriangles.html). Note:
area = abs((x1-x2)*(y1-y3)-(y1-y2)*(x1-x3)) for triangle (x1,y1)(x2,y2)(x3,y3)
Now if you sort all (x,y) points by their angle about the positive x axis, then we simply fix the first point, and consecutively walk through the remaining pairs and find the areas of those triangles. Left as an easy exercise why this sorting step is required (hint: draw a picture).
Solution to Part 2
Now the tough part is 2. Given that we have an active set of line segments enclosing the ball, how do we add a new line, and then adjust the size and number of line segments inside our active set. Note that at the beginning of the game there are precisely 4 lines in the active set which are the borders of the screen (this will be our base case if you like induction).
Now suppose we have an arbitrary active set containing the ball, and the user adds a new line, we assume it hits exactly two existing line segments and does not cross any line segments (by part 1 algorithm). Now we need to modify the active set. By algorithm 1, we know which line segments are hit by this point. So the only way that the active set can change is if both line segments hit by this point are in the active set (draw a picture to see this fact).
Now assume that the new line segment hits two lines inside the active set (this means it essentially splits the active set into two convex hulls). If we maintain our active set in a rotated order (that is to say we ensure that it is always sorted by angle about the positive x axis), we simply need to add our new points in a way that maintains this sorted ordering. So for instance suppose our points of the active set (collapsing line segments to single lines) are:
[(x1, y1), (x2, y2), (x3, y3), ..., (xn, yn), (x1, y1)]
And we want to add the new line segment ((x', y'), (x'', y'')), and our new set looks like:
[(x1, y1), (x2, y2), (x', y'), (x3, y3), ..., (xn, yn), (x'', y''), (x1, y1)]
Then there are now two convex hulls that are formed:
1. [(x1, y1), (x2, y2), (x', y'), (x'', y''), (x1, y1)]
2. [(x', y'), (x3, y3), ..., (xn, yn), (x'', y'')]
So the only remaining question is which is our new active set. Simply use algorithm 4.
Data Structures for Part 2
First we need the classes line segment and point (im avoiding implementing things like equals, hashcode for simplicity):
class Point {
public int x;
public int y;
}
class LineSegment {
public Point lineStart;
public Point lineEnd;
}
Now we can represent our active set datastructure:
class ActiveSet {
public List<Line> thesortedlist;
}
Now we first initialize our active set with four lines and denote the center of the screen as (0,0):
LineSegment TopBorder = new LineSegment(new Point(10, 10), new Point(-10, 10));
LineSegment LftBorder = new LineSegment(new Point(-10, 10), new Point(-10, -10));
LineSegment BtmBorder = new LineSegment(new Point(-10, -10), new Point(10, -10));
LineSegment RightBorder = new LineSegment(new Point(10, -10), new Point(10, 10));
ActiveSet activeset = new ActiveSet();
activeSet.theActiveLines.add(TopBorder);
activeSet.theActiveLines.add(LftBorder);
activeSet.theActiveLines.add(BtmBorder);
activeSet.theActiveLines.add(RightBorder);
Now say the user adds a line from point (0, 10) to (10, 0), so this is a diagonal (call it newSegment) and the new active set will look like:
------
- -
- -
- -
- -
- -
- B -
- -
-----------
Note the cut in the upper right corner, and B denotes the ball. Now by algorithm 1 we know that lines TopBorder and RightBorder are hit. Now both of these are inside the activeset (we can test membership faster by using a hashmap). Now we need to form two activesets as follows:
ActiveSet activesetLeft = new ActiveSet();
ActiveSet activesetRight = new ActiveSet();
Now in order to build these sets proceed as follows:
List<LineSegment> leftsegments = activeset.GetSegmentsBetween(TopBorder,
RightBorder);
List<RightSegment> rightsegments = activeset.GetSegmentsBetween(RightBorder,
TopBorder);
This function GetSegmentsBetween(LineSegment firstline, LineSegment secondline) should just locate firstline in the list and then return all elements until it finds secondline (this may need to do two passes through the list). It should not include these firstline or secondline in its return value. Now suppose we have activesetLeft and activesetright, we need to do the following:
activesetLeft.append(new Line(secondLine.lineStart, newSegment.lineStart));
activesetLeft.append(newSegment);
activesetLeft.append(new Line(newSegment.lineEnd, firstLine.lineEnd));
activesetRight.append(new Line(firstLine.lineStart, newSegment.lineEnd));
activesetRight.append(new Line(newSegment.lineEnd, newSegment.lineStart));
activesetRight.append(new Line(newSegment.lineStart, secondLine.lineEnd));
It is really hard to understand in code, but the order of everything above is important, sicne we want to maintain sorted going in counterclockwise order.
It is left as an exercise how you can speed this up (in fact you dont need to build two active sets, just first figure out if the ball is above or below the new segment and build the left or right activeset accordingly).

Related

How do I correctly describe this 4x4 square in my K-map?

I am trying to find a (SoP)-expression using the embedded K-map. I have a box of size 4x4 which is a permitted use however I am having a hard time understanding how I could implement it.
To me the 4x4 box represents that the output is always 1 independet on any of the variables. Then I'd like to use the 2x4 box to the right and produce:
1 OR (Qc AND !Qd), but this does not produce the correct result.
I can see several alternative ways to produce the correct result. My questions are specifically:
Why can't I use the 4x4 box, or perhaps, how do I represent it correctly?
How do I know when I can represent parts of the output as a 4x4 box?
Perhaps Im missing something more fundamental.
Thx in advance.
The point of placing rectangles in a K-map is to eliminate variables from an expression. When the result of a rectangle is the same for the variable values X and X', then the variable X is not needed and can be removed. You do this by extending an existing rectangle by doubling the size and eliminating exactly one variable, where every other variable stays the same. For the common/normal K-map with four variables this works with every such rectangle because in a way the columns/rows are labelled/positioned. See the following example:
The rectangle has eliminated the variables A and B, one variable at a time when the size of the rectangle has been extended/doubled. This results in the function F(A,B,C,D) = C'D'. But check the following K-map of four variables:
Notice that the columns for the D variable has been changed (resulting in a different function overall). When you try to extend the red rectangle to catch the other two 1 values as well, you are eliminating two variables at the same time (B and D). As you cannot grow the rectangle anymore, you are left with two rectangles, resulting in the function F(A,B,C,D) = BC'D' + B'C'D (which can be simplified to C' * (BD' + B'D)).
The practice in placing rectangles in the K-map isn't just placing the biggest rectangle possible, but to eliminate variables in the right way. To answer your questions, you can always start with the smallest rectangle and extend/double its size to eliminate one variable. See the following example:
The green rectangle grows in these steps:
Start with A'BC'D'E
Eliminate the (only) variable A by growing "down", resulting in BC'D'E
Eliminate the (only) variable D by growing "right", resulting in BC'E.
But now, the rectangle cannot grow/double its size anymore because that would eliminate the variable E, but also somehow eliminate the variable C. You cannot eliminate the variable E, because you have 0 values to the left of the green rectangle and 1 values to the right of the green rectangle (all in the left half of the K-map, where you have the value C'). The only way to increase/grow the rectangle is to get the "don't care" values to eliminate the B variable (not shown here).
The overall function for this K-map would be F(A,B,C,D,E) = C'E + DE' + CD' (from three 2x4 rectangles).

Building an MKPolygon using outer boundary of a set of coordinates - How do I split coordinates that fall on either side of a line?

I'm trying to build a MKPolygon using the outer boundary of a set of coordinates.
From what I can tell, there is no delivered functionality to achieve this in Xcode (the MKPolygon methods would use all points to build the polygon, including interior points).
After some research I've found that a convex-hull solves this problem.
After looking into various algorithms, the one I can best wrap my head around to implement is QuickHull.
This takes the outer lat coords and draws a line between the two. From there, you split your points based on that line into two subsets and process distance between the outer lats to start building triangles and eliminating points within until you are left with the outer boundary.
I can find the outer points just by looking at min/max lat and can draw a line between the two (MKPolyline) - but how would I determine whether a point falls on one side or the other of this MKPolyline?
A follow up question is whether there is a hit test to determine whether points fall within an MKPolygon.
Thanks!
I ended up using a variation of the gift wrap algorithm. Certainly not a trivial task.
Having trouble with formatting of the full code so I'll have to just put my steps (probably better because I have some clean up to do!)
I started with an array of MKPointAnnotations
1) I got the lowest point that is furthest left. To do this, I looped through all of the points and compared lat/lng to get lowest point. This point will definitely be in the convex hull, so add it to a NSMutableArray that will store our convex hull points (cvp)
2) Get all points to the left of the lowest point and loop through them, calculating the angle of the cvp to the remaining points on the left. Whichever has the greatest angle, will be the point you need to add to the array.
atan(cos(lat1)sin(lat2)-sin(lat1)*cos(lat2)*cos(lon2-lon1), sin(lon2-lon1)*cos(lat2))
For each point found, create a triangle (by using lat from new point and long from previous point) and create a polygon. I used this code to do a hit test on my polygon:
BOOL mapCoordinateIsInPolygon = CGPathContainsPoint(polygonView.path, NULL, polygonViewPoint, NO);
If anything was found in the hit test, remove it from the comparison array (all those on the left of the original array minus the hull points)
Once you have at least 3 points in your cvp array, build another polygon with all of the cvp's in the array and remove anything within using the hit test.
3) Once you've worked through all of the left points, create a new comparison array of the remaining points that haven't been eliminated or added to the hull
4) Use the same calculations and polygon tests to remove points and add the cvp's found
At the end, you're left with a list of points in that make up your convex hull.

Line intersections

Im writing an application that will calculate the focal length of a camera based on the lines that can be seen in the photograph. For instance, if you take a picture of a room, the ceiling line can be one straight line (horizontal), the floor can be another straight line (horizontal) and the wall can be the third straight line (vertical). The aim of my application is for the user to select these straight lines one at a time, and once 3 lines are selected, the lines will need to be intersected to form a "triangle".
My problem is that because the lines selected don't necessarily intersect, how do I extend a line until it intersects with another line? In my application I have the start and end positions of all 3 user selected lines (Vector2's). But how do I extend each line until it intersect with the other 2 lines?
If anyone needs an image to clarify what I mean, send me a reply and Ill upload one to Flickr
Say each line is represented by two vector2's: v1 and v2, all the points in that given line will be given by the equation: p(x) = v1 + x(v2-v1). Each line will have its equation in this form. For each pair of lines, you will have to find the value of x that gives you the same p(x) for both equations; p(x) will be the point of intersection of those two lines.
Sounds like you need to do 3 things.
Extend the lines to the end of the picture (in your code, not visible to the user).
Calculate line intersection. See this answer: detecting line intersection
on the user's end, extend the lines until the intersection point if there is one on the picture.

How to detect/handle touch events on curved regions?

I'm starting on a native iPad app (we can assume iOS 4.3+ if necessary) where I need to have a series of curved regions that bump up against each other.
I'd love some advice about the best way to handle this.
One thought I had was to use a WebView and just have a JPG and an HTML image map but I'd really prefer to use some kind of native UI element that supports curves.
Any recommendations?
We had a problem something like this. To resolve it, we created a black and white mask in Adobe Illustrator. You'll need to do this for each and every distinct region that you want.
Next, we exported this file. I can't remember the file export option, but basically you get a text file that has a load of path data that includes lines, bezier curves, etc.
We then took this file and wrote an importer that parsed it and created a CGPath.
The final stage is the easy bit. You get your touch point from UITouch and do a CGPathContainsPoint call.
Here's some pseudo code for this:
Skip lines until we get to one with "1 XR". That's the
indication of something meaningful in the subsequent line.
Split the line into an array of components using a separator of " ".
The last component of the line is the action. If it's "m" it's a path move, if it's "C", it's a bezier curve, and if it's "L" it's a line.
If it's a "Y" or a "V" then you need to get the previous line's components and parse as follows:
(a) Component count includes action, so we need to reduce this. E.g. 274.5600 463.6800 m
(b) If this line has four coordinates and the previous one has two, then it's a point node to a bezier node. Figure your bezier curve points as follows:
cp1x = previous line's components object at index 0
cp1y = previous line's components object at index 1
cp2x = this line's components object at index 0
cp2y = this line's components object at index 1
x = this line's components object at index 2
y = this line's components object at index 3
(c) Otherwise if this line has four coordinates and the previous line has four or six coordinates, figure as follows:
cp1x = this line's components object at index 0
cp1y = this line's components object at index 1
cp2x = this line's components object at index 2
cp2y = this line's components object at index 3
x = this line's components object at index 2
y = this line's components object at index 3
Where cp is "control point". So you have control point one and control point two with their respective x and y coordinates.
Create bezier paths that each represent separate regions (by doing lineToPoint or similar functions).
UIBezierPath *p1 = [UIBezierPath bezierPath];
[path1 lineToPoint:somePoint];
[pointArray1 addObject:NSStringFromCGPoint(somePoint)];
// create lots of points and close p1 path
Then find some triangulation algorithm for concave shapes that would turn each bezier path to an array of triangles (i.e. instead of storing array of bezier path point coordinates you'd store array of triangles' coordinates (array of array of points). Algorithm and explanations can be found in any game development forum or even on GameDev of stack exchange.
Repeat the bezier path creation and triangulation for each region.
Then having these arrays it's just a matter of simple iterations to check if certain point of interest is in one of these triangles.
update seems that #omz comment renders my answer useless, hence it's just a matter of creating bezier paths and calling method on each of them .)

connect line between two boxes avoiding passing others

I have several boxes (x,y,width,height) randomly scattered around, and some of them need to be linked from point (x1,y1) in box1 to point (x2,y2) in box2 by drawing a line. I am trying to figure a way to make such line avoid passing through any other boxes (other than box1 and box2) by drawing several straight interconnected lines to go around any box in the way (if it is not possible to go with one straight line). The problem is that I don't know an algorithm for such thing (let alone having a technical/common name for it). Would appreciate any help in the form of algorithm or expressed ideas.
Thanks
Assuming that the lines can't be diagonal, here's one simple way. It's based on BFS and will also find the shortest line connecting the points:
Just create a graph, containing one vertex for each point (x, y) and for each point the edges:
((x,y),(x+1,y)) ((x,y),(x-1,y)) ((x,y),(x,y+1)) ((x,y),(x,y-1))
But each of this edges must be present only if it doesn't overlap a box.
Now just do a plain BFS from point (x1,y1) to (x2,y2)
It's really easy to obtain also diagonal lines the same way but you will need 8 edges for each vertex, that are, in addition to the previouses 4:
((x,y),(x-1,y+1)) ((x,y),(x-1,y-1)) ((x,y),(x+1,y-1)) ((x,y),(x+1,y+1))
Still, each edge must be present only if it doesn't overlap a box.
EDIT
If you can't consider space divided into a grid, here's another possibility, it won't give you the very shortest path, though.
Create a graph, in which each box is a vertex and has an edge to any other box that can be reached without the line to overlap a third box. Now find the shortet path using dijkstra between box1 and box2 containing the two points.
Now consider each box to have a small countour that doesn't overlap any other box. This way you can link the entering and the exiting point of each box in the path found through dijistra, passing through the countour.
Put all (x,y) coords of the corners of the boxes in a set V
Add the start- and end coordinates to V.
Create a set of edges E connecting each corner that does not cross any box-side (except for the diagonals in the boxes).
How to check if a line crosses a box side can be done with this algorithm
Now use a path-finding algorithm of your choice, to find a path in the graph (V, E).
If you need a simple algorithm that finds the shortest path, just go with a BFS.
(This will produce a path that goes along the sides of some boxes. If this is undesirable, you could in step 1 put the points at some distance delta from the actual corners.)
If the edges may not be diagonal:
Create a large grid of lines that goes between the boxes.
Throw away the grid-edges that cross a box-side.
Find a path in the grid using a path-finding algorithm of your choice.