SceneKit `projectPoint` issue after updating sceneView's pointOfView position property - scenekit

I have a loop inside which I update the position of the sceneView's pointOfView property. I am surprised to see that after updating the position, the projectPoint method still returns the same values. What am I missing?
for phi in stride(from: 0, through: 90.0, by: 5) {
for theta in stride(from: 0.0, through: 180.0, by: 5) {
iter += 1
print("Iteration \(iter)")
let pos = sphericalToCartesian(r: 2.0, theta: theta, phi: phi)
let a = SCNVector3(3,4,5)
print("Projecting before updating pointOfView position \(sceneView.projectPoint(a))")
sceneView.pointOfView!.position = pos
print("Projecting after updating pointOfView position \(sceneView.projectPoint(a))")

The values aren't updating because you're setting sceneView.pointOfView.position many times in a single render pass. The values aren't applied by SceneKit which is why projectPoint() always returns the same value.
If you enclose the two projectPoint() calls in SCNTransaction blocks with zero duration you will see that the values are changed:
SCNTransaction.begin()
SCNTransaction.animationDuration = 0
let pos = SCNVector3(Float(arc4random()), Float(arc4random()), Float(arc4random()))
let a = SCNVector3(3,4,5)
print("Projecting before updating pointOfView position \(scnView.projectPoint(a))")
scnView.pointOfView!.position = pos
SCNTransaction.commit()
SCNTransaction.begin()
SCNTransaction.animationDuration = 0
print("Projecting after updating pointOfView position \(scnView.projectPoint(a))")
SCNTransaction.commit()
However, this will slow down the execution time of your loop because SCNTransaction atomically applies all the changes to the presentation scene graph. When I tested it took twice as long.
If that's an issue then I suppose you'll have to roll your own implementation of projectPoint()

Related

Merge all rectangles in an array with each other

I have an array of rectangles and I want to merge all overlapping and adjacent rectangles into each other. The final array should have no overlapping or adjacent rectangles. I tried doing the below code but some rectangles are still left over.
func combine_boxes(boxes: Array) -> void:
var merged := true
while merged:
merged = combine_all_overlapping_box(boxes)
func combine_all_overlapping_box(boxes: Array) -> bool:
var box: Rect2 = boxes[0]
# Remove our box
boxes.remove(0)
# Start from the back of the the array so removals are easier and don't invalidate the index
var merged := false
for j in range(boxes.size()-1, -1, -1):
var collider: Rect2 = boxes[j]
if overlap_adjacent(box, collider):
box = box.merge(collider)
# Remove collider box
boxes.remove(j)
merged = true
# Either add the merge box in, or the unaltered box we remove at the start
boxes.append(box)
return merged
This what I have been able to come up with:
func combine_rects(rects: Array) -> Array:
var result := rects.duplicate()
var merged := true
while merged:
merged = false
rects = result
result = []
while rects.size() > 0:
var current_rect:Rect2 = rects.pop_back()
for index in for index in range(rects.size() - 1, -1, -1):
var other_rect:Rect2 = rects[index]
if current_rect.intersects(other_rect, true):
merged = true
current_rect = current_rect.merge(other_rect)
rects.remove(index)
result.append(current_rect)
return result
Let us look at the inner loop first:
var current_rect:Rect2 = rects.pop_back()
for index in for index in range(rects.size() - 1, -1, -1):
var other_rect:Rect2 = rects[index]
if current_rect.intersects(other_rect, true):
merged = true
current_rect = current_rect.merge(other_rect)
rects.remove(index)
result.append(current_rect)
As you can see I remove an element from rects and store it in current_rect. Then iterate over the rest of the elements, comparing it with them. I use intersects with true as second argument so it also considers adjacent rects as intersecting… If they intersect, I merge them. And also remove the other element with rects.remove(index)… I iterate backwards so I don't have to adjust the index.
Since I set the current_rect with the result of the merge (current_rect = current_rect.merge(other_rect)), I can continue merging more rects into it in the same loop.
Finally I add whatever I ended up with to the result.
If we look at the mid loop, we see that I do this until I exhaust rects:
while rects.size() > 0:
var current_rect:Rect2 = rects.pop_back()
for index in for index in range(rects.size() - 1, -1, -1):
var other_rect:Rect2 = rects[index]
if current_rect.intersects(other_rect):
merged = true
current_rect = current_rect.merge(other_rect)
rects.remove(index)
result.append(current_rect)
Since I only remove from rects (either by pop_back or by remove) it must eventually be empty and the loop ends.
Now look at the broader picture:
func combine_rects(rects: Array) -> Array:
var result := rects.duplicate()
var merged := true
while merged:
merged = false
rects = result
result = []
while rects.size() > 0:
var current_rect:Rect2 = rects.pop_back()
for index in for index in range(rects.size() - 1, -1, -1):
var other_rect:Rect2 = rects[index]
if current_rect.intersects(other_rect):
merged = true
current_rect = current_rect.merge(other_rect)
rects.remove(index)
result.append(current_rect)
return result
We will continue looping as long as we merged something. The merged variable keeps track of that. I set it to true so the flow of execution enters the loop (the alternative would have been to make an infinite loop while true and use break).
For the loop we need to make sure merged starts false, and result is empty. So we do that. But also, we want to iterate over the results of the past iteration. Which is why I set rects = result. And that means that I need the starting rects in result. I took the opportunity to work on a copy (var result := rects.duplicate()).
You should be able to use it like this:
boxes = combine_rects(boxes)

Mutating range of For loops in Swift

I'm trying to set a dynamic range which changes while iterating inside a for loop. Aka: Jumping from index to index.
For example:
func func(c: [Int]) -> Int {
var currentIndex = 0
for i in currentIndex..<c.count
currentIndex += 3 //After changing the currentIndex value, will my "i" start from currentIndex?
}
So i starts with 0, then it will be 3, then 6 and so on...
When I run this code, "i" sums up as usual like i = 0, i = 1, i = 2... Can I mutate the iteration range?
As in comments, one powerful solution is using build in stride. Another solution is
while currentIndex < c.count
{
//your loop logic
currentIndex += 3
}
you don't need an 'i'.

pyQtGraph detect widget bounds and start scrolling

I'm trying to plot a live graph (a graph about the evolution of a stock trading) with pyQtGraph and have some questions I haven't been able to solve checking the examples:
I want the graph to start painting from left to right (this is what happens by default) and when it reaches right side of bounding box instead of resize it to make all new data fit I would like it to scroll making new data enter from the right and old data dissapearing to the left.
I know that appending data to a numpy array creates a new instance of the array. I don't want this. Is there any way to tell pyQtGraph plot to just get data in a range of the numpy array? For exmaple could I instantiate initially an array of 10000 floats and tell pyQtGraph to just plot the first 100 floats?
On the other hand I have come across that I could just modify the array in-place and shift the numbers to simulate the scrolling. Is there any way to make pyQtGraph use a numpy array as a ring? This way I would only need to tell that the graph starts at an index and everything would work without allocations, etc...
Here the code I have so far, pretty simple:
class Grapher(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
self.graphicsView.setTitle('My Graph')
self.graphicsView.setLabel('bottom', 'X axis')
self.graphicsView.setLabel('left', 'Y axis')
self.graphicsView.setLimits(xMin=0, yMin=0)
self.graphicsView.showGrid(x=True, y=True)
self.x=np.array();
self.y=np.array();
self._timer=QtCore.QTimer()
self._timer.timeout.connect(self.update)
self._timer.start(1000)
self._timerCounter=0;
def update(self):
self.x=np.append(self.x, [self._timerCounter]);
self.y=np.append(self.y, [math.sin(self._timerCounter)])
self.graphicsView.plot(self.x, self.y)
self._timerCounter+=1;
Thanks in advance.
This is how I would do it. If, say, you have an array of 1000 points but you only want to plot 200 points:
class Grapher(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
self.graphicsView.setTitle('My Graph')
self.graphicsView.setLabel('bottom', 'X axis')
self.graphicsView.setLabel('left', 'Y axis')
self.graphicsView.setLimits(xMin=0, yMin=0)
self.graphicsView.showGrid(x=True, y=True)
self.plot = self.graphicsView.plot()
self.ptr = 0
self.x = np.zeros(1000)
self.y = np.zeros(1000)
self.xPlot = np.zeros(200)
self.yPlot = np.zeros(200)
self._timer = QtCore.QTimer()
self._timer.timeout.connect(self.update)
self._timer.start(1000)
self._timerCounter = 0
def update(self):
xNew = self._timerCounter
yNew = math.sin(xNew)
self.y[self.ptr] = math.sin(self._timerCounter)
self.x[self.ptr] = self._timerCounter
if self.ptr < 200:
# Normal assignment
self.yPlot[self.ptr] = self.y[self.ptr]
self.xPlot[self.ptr] = self.x[self.ptr]
self.plot.setData(self.xPlot[1:self.ptr + 1],
self.yPlot[1:self.ptr + 1])
else:
# Shift arrays and then assign
self.yPlot[:-1] = self.yPlot[1:]
self.yPlot[-1] = self.y[self.ptr]
self.xPlot[:-1] = self.xPlot[1:]
self.xPlot[-1] = self.x[self.ptr]
self.plot.setData(self.xPlot, self.yPlot)
self.ptr += 1

Given an array of integers, resize the array with approximate equal distances

Given an array of numbers like this:
[0, 99, 299, 498, 901]
What algorithm could I use to resize that array into an array with approximately equal distances between them. Or said another way, resample with approximate greatest common multiples. So with the example above the Greatest Common Divisor is approximately 100, thus the result would be:
[0, 99, 200, 299, 400, 498, 600, 700, 800, 901]
Using the original values would be nice, and error bars can be set (above solution has error set to 2), but would also be happy with this result:
[0, 100, 200, 300, 400, 500, 600, 700, 800, 900]
Update 12 Jan 2017
Based on Redu's answer, here is the Swift version of his code:
var arr: [Int] = [0, 99, 299, 498, 901]
var diffs = [Int]()
var minGap: Int = 0
for x in 0..<arr.count-1 {
let gap = arr[x+1] - arr[x]
diffs.append(gap)
if minGap == 0 || minGap > gap {
minGap = gap
}
}
var resamples = [Int]()
for item in arr {
if let lastSample = resamples.last {
let n = Int((Float(item - lastSample) / Float(minGap)).rounded())
let g = (item - lastSample) / n
var inserts = [Int]()
for x in 0..<n-1 {
let newSample = lastSample + ((x+1) * g)
inserts.append(newSample)
}
resamples.append(item)
resamples.append(contentsOf: inserts)
} else {
resamples.append(item)
}
}
Essentially you want to use a least squares regression against an arithmetic progression.
An arithmetic progression can be parameterised with 3 terms: first term, last term, and common difference. These 3 terms would form the parameters of your objective function, which you will seek to minimise.
In each optimisation step, you'd need to pick which terms in the trial arithmetic progression need to be regressed against your original set. That will be quite challenging, but luckily both series will be sorted so this ought to be an O(N) traversal.
A constraint around the 3 terms would be a set that is typographically pleasing. For example, would 100, 200, 300 be preferred over 99, 198, 297 even if the source series is 99, 297?
A full answer would I feel be too broad - and is probably at least a week's work. But this is how I would embark on the project.
The following would be my solution in JS. I first find the minimum gap and then try to find how many of that would fit in-between each item and process accordingly without altering the original values.
Obviously for this algorithm to work the input array must be sorted in ascending order.
var arr = [0, 99, 299, 498, 901],
gap = Math.min(...Array(arr.length-1).fill().map((_,i) => arr[i+1]-arr[i])), // Find the minimum gap
res = arr.reduce((p,c,i) => { var n = Math.round((c-p[p.length-1])/gap); // Find howmany gaps are inbetween according to the minimum gap
g = Math.round((c-p[p.length-1])/n); // Calculate the average gap top apply
return i ? p.concat(Array(Math.round(n-1)).fill().map((_,i) => p[p.length-1] + (i+1)*g),c)
: p.concat(c);
},[]);
console.log(res);
Explanation:
gap = Math.min(...Array(arr.length-1).fill().map((_,i) => arr[i+1]-arr[i])),
First we set up a new array in size one less than the input array. (Array(arr.length-1)) first we initialize (.fill()) it with undefined elements and then .map() every element with arr[i+1]-arr[i]. So now we have the gaps array. Then we spread it into a Math.min() function as arguments. It's the Math.min(...Array( part. So now we have the minimum gap as 99 in the above given case.
res = arr.reduce((p,c,i) => { var n = Math.round((c-p[p.length-1])/gap);
g = Math.round((c-p[p.length-1])/n);
return i ? p.concat(Array(Math.round(n-1)).fill().map((_,i) => p[p.length-1] + (i+1)*g),c)
: p.concat(c);
},[]);
.reduce() part is slightly tough looking but it's easy. Our .reduce() operation takes a function as it's argument (mostly known as a callback function) and runs it with every iteration over the array items. This callback function is the part which starts with (p,c,i) => {... }. This is an arrow function. Which is essentially same with normal functions. x => x means function(x) { return x;} or x => {return x;}. In our case since we use braces to define the body of our function (due to multiple statements) we will have to use a return instruction.
Our .reduce() uses an initial value which is an empty array. It's the ,[]); part at the very end. The callback function, which reduce will invoke per array item, will be passed three arguments (p,c,i) The initial empty array gets assigned to the p (previous) argument, the current item gets assigned to the c argument and the current index gets assigned to the i argument per call.
In the body of our callback we define 2 variables. n and g.
n = Math.round((c-p[p.length-1])/gap);
p[p.length-1] returns the last element of the p array. So in the first turn; when i = 0, p[0] is undefined and Math.round((c-p[p.length-1])/gap); is a NaN (Not a Number) but we don't care because;
return i ? p.concat(Array(Math.round(n-1)).fill().map((_,i) => p[p.length-1] + (i+1)*g),c)
: p.concat(c);
The ternary conditional means that;
result = condition ? if true do this
: if false do this
So as you see depending on the condition it does either one of the instructions and returns the result. In our case the result is returned as the value of p.
So in our case if i == 0 (false value in JS) then only do p.concat(c) and return the new p value and continue with the next iteration (invoke callback with the new p, c and i values.
If i is not false (any value other than 0) then do like
p.concat(Array(Math.round(n-1)).fill().map((_,i) => p[p.length-1] + (i+1)*g),c)
Which means create an array in the size to take the gap many interim elements, initialize the array with undefineds and map each element with p[p.length-1] + (i+1)*g and concatenate this array to the p array and append c to the very end and then return the p array.
One thing to remind: p.concat(whatever...) instruction would return a new array consisting of the elements of p and the "items" of the arrays included as argument or the items itself included ar argument. I mean;
[1,2,3].concat([4,5,6],[7,8],9) would result [1,2,3,4,5,6,7,8,9]
So this should explain it.

Finding blocks in arrays

I was looking over some interview questions, and I stumbled onto this one:
There's an m x n array. A block in the array is denoted by a 1 and a 0 indicates no block. You are supposed to find the number of objects in the array. A object is nothing but a set of blocks that are connected horizontally and/or vertically.
eg
0 1 0 0
0 1 0 0
0 1 1 0
0 0 0 0
0 1 1 0
Answer: There are 2 objects in this array. The L shape object and the object in the last row.
I'm having trouble coming up with an algorithm that would catch a 'u' (as below) shape. How should i approach this?
0 1 0 1
0 1 0 1
0 1 1 1
0 0 0 0
0 1 1 0
One approach would use Flood Fill. The algorithm would be something like this:
for row in block_array:
for block in row:
if BLOCK IS A ONE and BLOCK NOT VISITED:
FLOOD_FILL starting from BLOCK
You'd mark items visited during the flood fill process, and track shapes from there as well.
This works in C#
static void Main()
{
int[][] array = { new int[] { 0, 1, 0, 1 }, new int[] { 0, 1, 0, 1 }, new int[] { 0, 1, 1, 1 }, new int[] { 0, 0, 0, 0 }, new int[] { 0, 1, 1, 0 } };
Console.WriteLine(GetNumber(array));
Console.ReadKey();
}
static int GetNumber(int[][] array)
{
int objects = 0;
for (int i = 0; i < array.Length; i++)
for (int j = 0; j < array[i].Length; j++)
if (ClearObjects(array, i, j))
objects++;
return objects;
}
static bool ClearObjects(int[][] array, int x, int y)
{
if (x < 0 || y < 0 || x >= array.Length || y >= array[x].Length) return false;
if (array[x][y] == 1)
{
array[x][y] = 0;
ClearObjects(array, x - 1, y);
ClearObjects(array, x + 1, y);
ClearObjects(array, x, y - 1);
ClearObjects(array, x, y + 1);
return true;
}
return false;
}
I would use Disjoint sets (connected components).
At the begining, each (i,j) matrix element with value 1 is one element set itself.
Then you can iterate over each matrix element and for each element (i,j) you should join each adjacent position set {(i+1,j),(i-1,j),(i,j+1),(i,j-1)} to (i,j) set if its value is 1.
You can find an implementation of disjoint sets at Disjoint Sets in Python
At the end, the number of diffrent sets is the number of objects.
I would use a disjoint-set datastructure (otherwise known as union-find).
Briefly: for each connected component, build an "inverse tree" using a single link per element as a "parent" pointer. Following the parent pointers will eventually find the root of the tree, which is used for component identification (as it is the same for every member of the connected component). To merge neighboring components, make the root of one component the parent of the other (which will no longer be a root, as it now has a parent).
Two simple optimizations make this datastructure very efficient. One is, make all root queries "collapse" their paths to point directly to the root -- that way, the next query will only need one step. The other is, always use the "deeper" of the two trees as the new root; this requires a maintaining a "rank" score for each root.
In addition, in order to make evaluating neighbors more efficient, you might consider preprocessing your input on a row-by-row basis. That way, a contiguous segment of 1's on the same row can start life as a single connected component, and you can efficiently scan the segments of the previous row based on your neighbor criterion.
My two cents (slash) algorithm:
1. List only the 1's.
2. Group (collect connected ones).
In Haskell:
import Data.List (elemIndices, delete)
example1 =
[[0,1,0,0]
,[0,1,0,0]
,[0,1,1,0]
,[0,0,0,0]
,[0,1,1,0]]
example2 =
[[0,1,0,1]
,[0,1,0,1]
,[0,1,1,1]
,[0,0,0,0]
,[0,1,1,0]]
objects a ws = solve (mapIndexes a) [] where
mapIndexes s =
concatMap (\(y,xs)-> map (\x->(y,x)) xs) $ zip [0..] (map (elemIndices s) ws)
areConnected (y,x) (y',x') =
(y == y' && abs (x-x') == 1) || (x == x' && abs (y-y') == 1)
solve [] r = r
solve (x:xs) r =
let r' = solve' xs [x]
in solve (foldr delete xs r') (r':r)
solve' vs r =
let ys = filter (\y -> any (areConnected y) r) vs
in if null ys then r else solve' (foldr delete vs ys) (ys ++ r)
Output:
*Main> objects 1 example1
[[(4,2),(4,1)],[(2,2),(2,1),(1,1),(0,1)]]
(0.01 secs, 1085360 bytes)
*Main> objects 1 example2
[[(4,2),(4,1)],[(0,3),(1,3),(2,3),(2,2),(2,1),(1,1),(0,1)]]
(0.01 secs, 1613356 bytes)
Why not just look at all the adjacent cells of a given block? Start at some cell that has a 1 in it, keep track of the cells you have visited before, and keep looking through adjacent cells until you cannot find a 1 anymore. Then move onto cells that you have not looked yet and repeat the process.
Something like this should work:
while array has a 1 that's not marked:
Create a new object
Create a Queue
Add the 1 to the queue
While the queue is not empty:
get the 1 on top of the queue
Mark it
Add it to current object
look for its 4 neighbors
if any of them is a 1 and not marked yet, add it to queue

Resources