Today we are drawing polygons on a MKMapView. We use the following pseudocode to draw polygons.
CGContextMoveToPoint
CGContextAddLineToPoint
CGContextAddLineToPoint
CGContextAddLineToPoint
CGContextClosePath
CGContextFillPath
The result could potentially look like this:
We get the data one row at a time, the colors are given to the cell based on the data we receive. Is there a way or polygon reduction algorithms that would group all the same colored polygons together (assuming they intersect) to give me one big polygon? So in this example all the reds would one big polygon.
CoreGraphics can handle concave polygons natively, so the main part of the problem is a flood fill to work out the boundaries of the filled area.
Thinking extemporaneously, a naive algorithm could be to associate edge flags with each cell. An edge flag is set if that edge is part of the exterior of the polygon. Flags are shared by the two cells that meet at that edge.
Pick any cell and set all four edge flags. Reset the edge flags on all other cells. Then write a recursive method that, for each cell:
tests in turn whether each edge flag is set;
if a flag is set, checks whether the cell that shares that edge is of the same colour;
if it is, inverts the edge flags of that cell and recurses to it.
The inversion is the same as saying "connect to any cells you're known to be next to, set any edges that are next to cells we haven't looked at yet to be part of the boundary".
The recursion could get hundreds of items deep, so it might be worth keeping a list of cells to consider and adding to that list rather than recursing, just as a matter of implementation. It shouldn't matter what order you visit the cells in, so the outcome should be the same.
Once you've run out of cells to visit, you can reconstruct the entire boundary by walking around it from any flagged edge. The only slight complexity will be when you get to a diagonal meeting of cells, like where the yellow and green cells touch between your fourth and fifth columns. You need to apply the logic that you move from the current edge to the next one with which it shares both a vertex and a cell of the correct colour.
This is a job for the rectangle drawing functions, not the path drawing functions. See CGContextFillRect(), CGContextStrokeRect() and CGContextFillRects(). They will be much faster.
Related
I am using matplotlib.pyplot.plotfile to read and plot data from a comma-separated-file.
The file has a number of locations where rows are missing (on purpose). I want the plot to stop at this point, lift the pen, and restart a new line after the break. Instead it is always ignoring the missing line, and joining the data points to make a continuous plot. Can I stop this from happening?
Context
Writing to code to format a chart (all of which should be done by Microsoft, but that’s separate).
Am now positioning the legend. Taking a 9×9 block of possible positions, and counting the data points underneath each. As a fragment of the code: (ax.MaximumScale - ax.MinimumScale) * co.Chart.Legend.Width / co.Chart.PlotArea.InsideWidth.
Also coping with lines underlapping and text boxes overlapping the possible legend positions: same idea, more complexity.
Question
Obviously, all this works better if the legend is as small as possible, as that gives a greater likelihood of finding a location with zero ’lapping.
If .Legend.Width is too small, then the individual legend texts (the Series.Name’s) wrap onto ≥2 lines, which isn’t wanted. So VBA could interval bisect to find the smallest .Legend.Width for which there isn’t line wrapping. But how can the VBA code ‘see’|‘detect’|‘know’ of the existence of the line wrapping?
And mutatis mutandis for .Legend.Height: if that’s too small, some legend entries aren’t shown. How can the VBA code ‘see’|‘detect’|‘know’ that a height is too small?
Thank you.
PS: I expect that the correct answer is that “VBA cannot ‘see’|‘detect’|‘know’ either of these.” Please refute this expectation.
If you create your own legend, using a text box, you have better options when it comes to sizing and flow control. This will create a new set of challenges, but it might be easier to handle.
I’m trying to find a solution for the 2-dimensional packing problem using Excel (Formulas, Solver, VBA).
But apart from finding said solution, I would like to bring back this topic as base for discussion, because I realized during my extended web-searches that this problem (or variations of it) creates headaches for many people – novice and professional users.
The explanation for my problem:
I am trying to fit rectangular packages in rectangular containers. Usually there is one larger box and 2-5 smaller boxes to ship.
On average, there is still capacity of 30-50% left in the containers, so I want to calculate how many additional standardized boxes would fit in this free space to fill up the container.
There are no constraints, as long as the boxes fit into the container.
Height and weight are irrelevant.
The Boxes can be rotated by 90°.
One 40’ container is 1203cm long and 233cm wide.
The standardized boxes are 85cm x 70cm
The other boxes have different sizes.
I checked bin-packing algorithms but as of now I was not able to implement any solution in excel. I’d prefer a way to calculate this using Excel Solver or VBA, but my VBA-programming knowledge is limited.
The knapsack problem does not apply here in my opinion, although it is mentioned many times in this context.
In my case, I would be happy with a solution giving me something like: “You can fit at least x additional Boxes in the container”. Some inaccuracy does not matter – meaning up to 25% less boxes than possible. Too much boxes, on the other hand, are a no-go.
Now, do you guys have any idea how to get started here or even accomplish this? Maybe there is even a super-simple approximation I don’t know of?
Thanks!
UPDATE
After quite some time, I finally found some hours to get into this problem again.
I read Erwin Kalvelagen ‘s Blogposts and some papers on bin packing algorithms.
Also, the solver option is off the table.
I decided to go for a Bottom-Left-Algorithm (BLT) with some restraints (not just greedy).
Quick explanation of the BLT-Algorithm: Each box is placed in the bottom-most and left-most possible position in a given area (container). When a box is placed, it creates two new “corners” where the remaining boxes can be placed. Initially, the boxes are sorted by length (to start with the longest box) and place them in a 2-dimensional array. Then the starting point will be set in an Array (x, y coordinates) – the first coordinates are obviously 0, 0 as we start in an empty container. Then the algorithm would try to place the first box in the bottom-left corner with coordinates 0, 0 – which of course works perfectly. Then the starting cords would be replaced by the coords of one of the new corners and the coords of the other corner will be added to C. this would loop until all non-standard boxes are loaded. Then the algorithm would add standardized boxes if possible (and count them). The loop would end, if adding more boxes is not possible anymore due to constraints.
The dimension of the non-standard boxes will be entered in a worksheet - one box per row. The dimensions of the container and the standardized boxes will be written there as well.
Constraints would be, that no box can overlap another and all boxes would have to inside the container. Although rotation is practically possible, it is not necessary to implement it in the code as I am trying to orient the packages along the container.
Here is some pseudo code of the BLT-Algorithm I found:
**Procedure BLF(width, height,maxWidth)**
begin
initialize the arrays x and y
initialize the list and add the null point
for all rectangles
initialize choosePoint as impossible
while choosePoint is impossible and j < length of list
if the rectangle could be placed in a specific point
choose the point
endif
endwhile
if choosePoint is possible
update the arrays x and y
remove the point from the position choosePoint
from list
add the points (xi+width,yi),(xi,yi+height) to the points list
else
if (width > maxWidth) the problem has no solution
else xi = 0 and yi = max(heightk + yk)
where k 2 {1, . . . , i − 1}
endif
endif
endfor
solutions: the arrays x and y with (xi, yi)
the coordinates of rectangle i
end
Now, although I know a lot (like really A LOT) more about packing algorithms I am still not very experienced with VBA. Especially not with implementing algorithms.
So again I would be happy for any help you can give me to get started with the implementation.
So I started off with this (I know it’s really nothing, but I find it quite difficult):
Sub BLT1()
Dim Boxes As Variant, i As Integer, j As Integer ‘’Boxes dimensions
Dim Cntnr As Variant, a As Integer, b As Integer ‘’Container dimensions
Dim BLPoints As Variant ‘’Array with coordinates of bottom-left corners
Boxes = Range("B11:C15")
Cntnr = Range("D2:E2")
‘’Now I would like to add the first coordinates (0, 0) to the BLPoints
‘’Then I want to pick the first box and fit it in the container at the (0, 0) coordinates
‘’Then I want to update the BLPoints array with the new coordinates
…
End Sub
I’m looking forward to any constructive feedback and advice!
This is not a very easy problem. Some possible approaches are:
A MIP (Mixed Integer Programming) Model. The most complex part are the no-overlap constraint. For each box in the container we need to make sure it does not occupy space used by another box. The MIP approach has the advantage that we can find optimal solutions, or very good solutions with an indication how much we are away from a possible best solution (i.e. an indication of the quality of the solution).
A constraint programming model. Similar to the MIP model, but some constructs are easier to handle (i.e. the OR construct needed to formulate the no-overlap constraints).
A heuristic or meta-heuristic approach.
I implemented quickly a MIP model and it turns out you can get optimal or near-optimal solutions quite quickly. The solution below was found in less than a minute using a commercial MIP solver:
The yellow boxes are the required non-standard boxes and the blue ones are the optional standard boxes.
See here for more information about these no-overlap constraints. Here are the no-overlap constraints for this problem.
i've got to this stage:
where i can find the numbers in the above image but i need to cut them out so i can retain the order etc. but the as the number increases the spacing changes and the position of the number?
so i think it should be a find a white PX the continue until it find a solid black col and then use the points to do a simple cut any help would be great.
A simple solution would be this:
Find the first upmost horizontal line which contains white pixels
From that line find the first horizontal line which contains only black pixels
Those two lines are your upper and lower borders.
Between this borders proceed like this:
Find the first most left vertical line which contains white pixels
From that line find the last vertical line which contains only black pixels and which comes directly after a line with white pixels.
Those two lines are your left and right borders.
The steps to separate single numbers can be performed analogously.
If you need to identify which numbers are in your picture, I recommend using specialized computer vision libraries.
Some VB.net pseudo code to get you going:
Sub FindTopBorder(image As MyImage) As Integer
For y = 0 to image.Height - 1
For x = 0 to image.Width - 1
Dim pixel = image.GetPixel(x, y)
If ('Check if pixel is white here with RGB or Color') Then
Return y
End If
Next
Next
' Just in case there are no white pixels or use an exception instead
Return -1
End Sub
I would start looking into Connected component segmentation. You find a pixel which is within a character (number). Then run the connected component algorithm which finds all connected pixels under specific set of rules (e.g. slight deviation in color, stop at hard borders etc).
http://en.wikipedia.org/wiki/Connected-component_labeling
If you can use libraries, I'm sure OpenCV or similar libraries support this out of the box.
//edit
I see you need VB.net. Probably it is easiest to port some algorithm to VB or create one yourself.
See e.g. http://www.codeproject.com/Articles/336915/Connected-Component-Labeling-Algorithm
What to expect
Input
An image containing two shapes:
Output
Now each is separated into single images.
I'm writing a code generation tool using VBA in Excel (don't ask why—long story). I need to be able to "parse" a flowchart.
The problem is that Excel allows shapes to contain text, with the exception of connectors: lines and arrows can't contain text. To label an arrow, you just put a text box on top of it—but the box isn't "attached" to the arrow in a way that VBA can easily capture.
For example, a user might draw something like this:
Within my VBA code, I can use ActiveSheet.Shapes to find that the flowchart contains seven shapes: there are five boxes (the two labels are just boxes with no border) and two arrows. Then Shape.TextFrame2 will tell me what's written inside each box, and Shape.ConnectorFormat will tell me which box goes at the start and end of each arrow.
What I need is code that can deduce:
Label A belongs to the arrow from Box 1 to Box 2
Label B belongs to the arrow from Box 1 to Box 3
I can think of three ways of doing this, none of them satisfactory.
Ask the user to group each label with its corresponding arrow.
Find out the coordinates of the endpoints of each arrow, then
calculate which arrows pass through which labels.
Find out the coordinates of the corners of each box, then calculate
which labels lie between which pairs of boxes.
Method 1 makes things easier for the programmer but harder for the user. It opens up a lot of potential for user error. I don't see this as an acceptable solution.
Method 2 would be reasonably easy to implement, except that I don't know how to find out the coordinates!
Method 3 is doable (Shape.Left etc will give the coordinates) but computationally quite messy. It also has potential for ambiguity (depending on placement, the same label may be associated with more than one arrow).
Note that methods 2 and 3 both involve trying to match every label with every arrow: the complexity is quadratic. Typical applications will have 10–50 arrows, so this approach is feasible, if somewhat inelegant.
Does anyone have a better idea? Ideally it would be something that doesn't involve coordinate geometry and complicated logic, and doesn't involve asking users to change the way they draw flowcharts.
Edited to add: example 2 in response to Tim Williams
Here's a label whose bounding box intersects the bounding box of both arrows, and whose midpoint isn't inside the bounding box of either arrow. Visually it's easy for a human to see that it belongs with the left arrow, but programmatically it's hard to deal with. If I can find out the coordinates of the arrows' endpoints, then I can calculate that one arrow passes through the label's box but the other doesn't. But if all I have is the bounding rectangles of the arrows, then it doesn't work.
Interesting problem. What if you considered the range covered by the arrow and the range covered by the textbox and matched them up based on the most overlap.
Sub ListShapes()
Dim shp As Shape
Dim shpArrow As Shape
Dim vaArrows As Variant
Dim i As Long
Dim rIntersect As Range
Dim aBestFit() As String
Dim lMax As Long
vaArrows = Split("Straight Arrow Connector 7,Straight Arrow Connector 9", ",")
ReDim aBestFit(LBound(vaArrows) To UBound(vaArrows))
For i = LBound(vaArrows) To UBound(vaArrows)
Set shpArrow = Sheet1.Shapes(vaArrows(i))
lMax = 0
For Each shp In Sheet1.Shapes
If shp.Name Like "Label*" Then
Set rIntersect = Intersect(Sheet1.Range(shp.TopLeftCell, shp.BottomRightCell), _
Sheet1.Range(shpArrow.TopLeftCell, shpArrow.BottomRightCell))
If Not rIntersect Is Nothing Then
If rIntersect.Count > lMax Then
lMax = rIntersect.Count
aBestFit(i) = shp.Name
End If
End If
End If
Next shp
Next i
For i = LBound(vaArrows) To UBound(vaArrows)
Debug.Print vaArrows(i), aBestFit(i)
Next i
End Sub
I tested this with the five box-two arrow setup and nothing more complicated. I put my two arrows in an array, but I assume you have ways to identify the arrows. I also named my untethered boxes "Label x" so I could identify them, but again I assume you have something more sophisticated.
The code loops through every arrow. Inside that loop, it loops through every shape. If it's a label, then it counts the cells in the intersection of the two ranges. Whichever has the most is stored in the best fit array.
It would be nice if you had a reasonable corpus of flow charts to test this to see where the pitfalls are. I don't think this is necessarily better than use the coordinates, just a different approach.
You can find the coordinates of the arrow's endpoints as follows.
First of all, the .Left, .Top, .Width and .Height properties describe the bounding rectangle of the arrow, as Tim Williams points out.
Next, check the .HorizontalFlip and .VerticalFlip properties. If both are false, then the arrow runs from top left to bottom right in its bounding rectangle. That is, the beginning of the arrow has coordinates (.Left,.Top) and the end has coordinates (.Left+.Width,.Top+.Height).
If either *.Flip is true, then the coordinates need to be swapped around as appropriate. E.g., if .HorizontalFlip is true but .VerticalFlip false, then the arrow runs from (.Left+.Width,.Top) to (.Left,.Top+.Height).
As far as I can tell, this is not documented anywhere on MSDN. Thanks to Andy Pope for mentioning it at excelforums.com.
Given this, method 2 seems like the best approach.