Vectorised Marching cubes (squares) - connecting the lines into curves - python

I am drawing a metaball with marching cubes (squares as it is a 2d) algorithm.
Everything is fine, but I want to get it as a vector object.
So far I've got a vector line or two from each active square, keeping them in list lines. In other words, I have an array of small vector lines, spatially displaying several isolines (curves) - my aim is to rebuild those curves back from lines.
Now I am stuck with fast joining them all together: basically I need to connect all lines one by one all together into several sequences (curves). I don't know how many curves (sequences) will be there, and lines could be in different directions and I need to process lines into sequences of unique points.
So far I wrote something obviously ugly and half-working (here line is a class with list of points as attribute points, and chP is a function checking if points are close enough, t definies this 'enough') :
def countur2(lines):
'''transform random list of lines into
list of grouped sequences'''
t = 2 # tolerance
sqnss = [[lines[0]]] # sequences
kucha = [lines[0]] #list of already used lines
for l in lines:
for i,el in enumerate(lines):
print 'working on el', i
ss = sqnss[-1][0]
ee = sqnss[-1][-1]
if el not in kucha:
if chP(el.points[0],ee.points[1],t):
sqnss[-1].append(el)
kucha.append(el)
break
elif chP(el.points[1],ee.points[1],t):
sqnss[-1].append(el.rvrse())
kucha.append(el)
break
elif chP(el.points[1],ss.points[0],t):
sqnss[-1] = [el] + sqnss[-1]
kucha.append(el)
break
elif chP(el.points[0],ss.points[0],t):
sqnss[-1] = [el.rvrse()] + sqnss[-1]
kucha.append(el)
break
print 'new shape added, with el as start'
sqnss.append([el])
kucha.append(el)
#return sqnse of points
ps = []
for x in sqnss: ps.append([el.points[0] for el in x])
return ps
I know this is such a big question, but please give me any clue on right direction to handle this task

A first option is to number all cell sides uniquely, and associate to every vector the pair of edges it joins.
Enter all pairs in a dictionary, both ways: (a,b) and (b,a). Then, starting from an arbitrary pair, say (a,b), you will find the next pair through b, say (b,c). You will remove both (b,c) and (c,b) from the dictionary, and continue from c, until the chain breaks on a side of the domain, or loops.
A second option is to scan the whole domain and when you find a cell crossed by an isocurve, compute the vector, and move to the neighboring cell that shares an edge crossed by the isocurve, and so on. To avoid an infinite scanning, you will flag the cell as already visited.
By contrast with the first approach, no dictionary is required, as the following of the chain is purely based on the local geometry.
Beware that there are two traps:
cells having one or more corner values equal to the iso-level are creating trouble. A possible cure is by slightly modifying the values corner; this will create a few tiny vectors.
cells can be crossed by two vectors instead of one, and require to be visited twice.

Related

Need Help Trying to Simplify this algorithm to map points on an arbitrarily large 2d plane to unique integers

So like the title says I need help trying to map points from a 2d plane to a number line in such a way that each point is associated with a unique positive integer. Put another way, I need a function f:ZxZ->Z+ and I need f to be injective. Additionally I need to to run in a reasonable time.
So the way I've though about doing this is to basically just count points, starting at (1,1) and spiraling outwards.
Below I've written some python code to do this for some point (i,j)
def plot_to_int(i,j):
a=max(i,j) #we want to find which "square" we are in
b=(a-1)^2 #we can start the count from the last square
J=abs(j)
I=abs(i)
if i>0 and j>0: #the first quadrant
#we start counting anticlockwise
if I>J:
b+=J
#we start from the edge and count up along j
else:
b+=J+(J-i)
#when we turn the corner, we add to the count, increasing as i decreases
elif i<0 and j>0: #the second quadrant
b+=2a-1 #the total count from the first quadrant
if J>I:
b+=I
else:
b+=I+(I-J)
elif i<0 and j<0: #the third quadrant
b+=(2a-1)2 #the count from the first two quadrants
if I>J:
b+=J
else:
b+=J+(J-I)
else:
b+=(2a-1)3
if J>I:
b+=I
else:
b+=I+(I-J)
return b
I'm pretty sure this works, but as you can see it quite a bulky function. I'm trying to think of some way to simplify this "spiral counting" logic. Or possibly if there's another counting method that is simpler to code that would work too.
Here's a half-baked idea:
For every point, calculate f = x + (y-y_min)/(y_max-y_min)
Find the smallest delta d between any given f_n and f_{n+1}. Multiply all the f values by 1/d so that all f values are at least 1 apart.
Take the floor() of all the f values.
This is sort of like a projection onto the x-axis, but it tries to spread out the values so that it preserves uniqueness.
UPDATE:
If you don't know all the data and will need to feed in new data in the future, maybe there's a way to hardcode an arbitrarily large or small constant for y_max and y_min in step 1, and an arbitrary delta d for step 2 according the boundaries of the data values you expect. Or a way to calculate values for these according to the limits of the floating point arithmetic.

Finding intersectionpoints that form rectangles

I have a bunch of lines described by their direction as well as a point that describes its origin. I have to combine these lines to make them form rectangles that can lie within eachother, but their edges cannot overlap. I also know that the origin of the lines lie within an edge of a rectangle, but it does not necessarily lie in the middle of that edge. Basically the input I have could be something like this:
And what I'm trying to achieve looks something like this:
Where every line is now described by the points where it intersected the other lines to form the correct rectangles.
I'm looking for an algorithm that finds the relevant intersection points and links them to the lines that describe the rectangles.
First of all, this problem as it was stated, may have multiple solutions. For example I don't see any constraint that invalidates the following:
So, you need to define an objective, for example:
maximize total covered are
maximize number of rectangles
maximize number of used lines
...
Here I'm trying to maximize number of rectangle using a greedy approach. Keep in mind that a greedy algorithm never guarantees to find the optimum solution, but finds a sub-optimal one in a reasonable time.
Now, there are two steps in my algorithm:
Find all possible rectangles
Select a set of rectangles that satisfy constrains
Step 1: Find all possible rectangles
Two vertical lines (l & r) plus two horizontal lines (b & t) can form a valid rectangle if:
l.x < r.x and b.y < t.y
l.y and r.y are between b.y and t.y
b.x and t.x are between l.x and r.x
In the following pseudocode, Xs and Ys are sorted lists of vertical and horizontal line respectively:
function findRectangles
for i1 from 1 to (nx-1)
for i2 from (i1+1) to nx
for j1 from 1 to (ny-1)
if (Ys[j1].x>=Xs[i1].x and
Ys[j1].x<=Xs[i2].x and
Ys[j1].y<=Xs[i1].y and
Ys[j1].y<=Xs[i2].y)
for j2 from (j1+1) to ny
if (Ys[j2].x>=Xs[i1].x and
Ys[j2].x<=Xs[i2].x and
Ys[j2].y>=Xs[i1].y and
Ys[j2].y>=Xs[i2].y)
add [i1 j1 i2 j2] to results
end if
end for
end if
end for
end for
end for
end
Step 2: Select valid rectangles
Valid rectangles, as stated in the problem, can not overlap partially and also can not share an edge. In previous step, too many rectangles are found. But, as I said before, there may be more than one combination of these rectangles that satisfy constraints. To maximize the number of rectangle I suggest the following algorithm that tends to accept smaller rectangles:
function selectRects( Xs, Ys, rects )
results[];
sort rectangles by their area;
for i from 1 to rects.count
if (non of edges of rects[i] are eliminated)&
(rects[i] does not partially overlap any of items in results)
add rects[i] to results;
Xs[rects[i].left].eliminated = true;
Xs[rects[i].right].eliminated = true;
Ys[rects[i].bottom].eliminated = true;
Ys[rects[i].top].eliminated = true;
end if
end for
end

Finding the coordinates to randomly distribute images on a display without overlap

I have the following code which randomly generates a list of (X,Y) tuples:
import random
coords = []
for i in range(10):
x = random.randint(85,939)
y = random.randint(75,693)
coords.append((x,y))
In the final list, the X values of each tuple are considered to overlap if the absolute difference between them is less then 85, and the Y values are considered to overlap if the absoulte difference is less than 75. How can I make sure that none of the tuples in the final list will overlap in both dimensions?
The easiest way to do this is to just keep sampling and discarding coordinates which will create overlap. This will however become very inefficient when you come close to filling the available space. If that's not an issue you should go with this solution.
A bit more efficient and as far as I can tell statistically equivalent is to sample one coordinate, like the row, first. Then compute the occupied area in that row and sample from the remaining positions if there are any.
To avoid the same problem as in the easy solution, if there are no available spaces in a row, it should be removed from the possible sampling outcomes for the row (plus the margin of 75 in both directions).
Ideally, you don't compute the occupied regions each time but you keep a mapping from the row to the occupied space in that row and the amount of non-full rows and just update this mapping when inserting new images. You will need storage for n_rows + 1 extra numbers.
To clarify: When sampling from a restricted space just subtract the occupied positions and get a sampling result n. Then find the correct position for n by walking along the coordinate axis n steps, skipping all the occupied positions.

Change next list element during iteration?

Imagine you have a list of points in the 2D-space. I am trying to find symmetric points.
For doing that I iterate over my list of points and apply symmetry operations. So suppose I apply one of these operations to the first point and after this operation it is equal to other point in the list. These 2 points are symmetric.
So what I want is to erase this other point from the list that I am iterating so in this way my iterating variable say "i" won't take this value. Because I already know that it is symmetric with the first point.
I have seen similar Posts but they remove a value in the list that they have already taken. What I want is to remove subsequent values.
Whatever symmetric points turn out to be True add them to a set, since set maintains unique elements and look up is O(1) you can use if point not in set condition.
if point not in s:
#test for symmetry
if symmetric:
s.add(point)
In general it is a bad idea to remove values from a list you are iterating over. There are, however, another ways to skip the symmetric points. For example, you can check for each point if you have seen a symmetric one before:
for i, point in enumerate(points):
if symmetric(point) not in points[:i]:
# Do whatever you want to do
Here symmetric produces a point according to your symmetry operation. If your symmetry operation connects more that two points you can do
for i, point in enumerate(points):
for sympoint in symmetric(point):
if sympoint in points[:i]:
break
else:
# Do whatever you want to do

Sorting points on multiple lines

Given that we have two lines on a graph (I just noticed that I inverted the numbers on the Y axis, this was a mistake, it should go from 11-1)
And we only care about whole number X axis intersections
We need to order these points from highest Y value to lowest Y value regardless of their position on the X axis (Note I did these pictures by hand so they may not line up perfectly).
I have a couple of questions:
1) I have to assume this is a known problem, but does it have a particular name?
2) Is there a known optimal solution when dealing with tens of billions (or hundreds of millions) of lines? Our current process of manually calculating each point and then comparing it to a giant list requires hours of processing. Even though we may have a hundred million lines we typically only want the top 100 or 50,000 results some of them are so far "below" other lines that calculating their points is unnecessary.
Your data structure is a set of tuples
lines = {(y0, Δy0), (y1, Δy1), ...}
You need only the ntop points, hence build a set containing only
the top ntop yi values, with a single pass over the data
top_points = choose(lines, ntop)
EDIT --- to choose the ntop we had to keep track of the smallest
one, and this is interesting info, so let's return also this value
from choose, also we need to initialize decremented
top_points, smallest = choose(lines, ntop)
decremented = top_points
and start a loop...
while True:
Generate a set of decremented values
decremented = {(y-Δy, Δy) for y, Δy in top_points}
decremented = {(y-Δy, Δy) for y, Δy in decremented if y>smallest}
if decremented == {}: break
Generate a set of candidates
candidates = top_lines.union(decremented)
generate a new set of top points
new_top_points, smallest = choose(candidates, ntop)
The following is no more necessary
check if new_top_points == top_points
if new_top_points == top_points: break
top_points = new_top_points</strike>
of course we are in a loop...
The difficult part is the choose function, but I think that this
answer to the question
How can I sort 1 million numbers, and only print the top 10 in Python?
could help you.
It's not a really complicated thing, just a "normal" sorting problem.
Usually sorting requires a large amount of computing time. But your case is one where you don't need to use complex sorting techniques.
You on both graphs are growing or falling constantly, there are no "jumps". You can use this to your advantage. The basic algorithm:
identify if a graph is growing or falling.
write a generator, that generates the values; from left to right if raising, form right to left if falling.
get the first value from both graphs
insert the lower on into the result list
get a new value from the graph that had the lower value
repeat the last two steps until one generator is "empty"
append the leftover items from the other generator.

Categories

Resources