Konva - Line Drag - drag

I have array of draggable lines. When drag is completed I want to have location of each points of the the line updated in array. I was getting some help from https://konvajs.org/docs/react/Transformer.html as these lines will require transformation as well.
However, when I drag a line it displace it self if I update the array that is being used to draw the lines. I am not able to understand why it is doing that and how to solve this.
I have sample Codesandbox here - https://codesandbox.io/s/nice-fog-rrbpj4

While you drag, x and y properties of Konva.Line are changed.
If you apply these moves from x, y properties to points attribute, you need to reset x and y back to original values 0, 0.
const points = shapeProps.points.map((p) => {
return shapeRef.current.getAbsoluteTransform().point({
x: p.x,
y: p.y
});
});
shapeRef.current.position({ x: 0, y: 0 });
onChange({
...shapeProps,
points
});
https://codesandbox.io/s/inspiring-bash-5whv01

Related

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

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()

Capybara click method passing parameters results in not a key modifier error

I'm trying to use the Capybara Node::Element click with the offset as defined in the Rdoc.
Basically, I want to use the click method on an element, but clicking NOT on the center of the element, but offset to the left. I don't want to do the action.move_to methods.
#click(*key_modifiers = [], offset = {x: nil, y: nil}) ⇒ Capybara::Node::Element
Click the Element
Parameters:
*key_modifiers (Array<:alt, :control, :meta, :shift>) (defaults to: [])
— Keys to be held down when clicking
offset (Hash) (defaults to: {x: nil, y: nil}) — x and y coordinates
to offset the click location from the top left corner of the element.
If not specified will click the middle of the element.
I'm just not sure how to use this if I want the default key_modifier ('[ ]'). When I try this, I get a not a key modifier error.
elem = find(some_xpath)
elem.click([], {x: -20, y: nil})
ArgumentError:
[] is not a modifier key, expected one of [:control, :shift, :alt,
:command, :meta]
I tried skipping [] and it doesn't seem to do the offset
elem.click({x: -100 y:nil})
You need to specify both x any y, you can't specify nil for one of them
click(x: -100, y: 0)

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.

How to get x and y coordinate of mouseClick

I am trying to achieve painting a rectangle around the center of mouseClick position. For that i feel i need to obtain the x and y coordinate as an int.
(This is edited code and e.X or e.Y is the solutions to this question)
let mouseClick (e: MouseEventArgs) =
let x = e.X
let y = e.Y
let coords = [|System.Drawing.Point((x-10),(y-10));
System.Drawing.Point((x-10),(y+10));
System.Drawing.Point((x+10),(y+10));
System.Drawing.Point((x+10),(y-10));
System.Drawing.Point((x-10),(y-10))|]
window.Paint.Add(fun e -> e.Graphics.DrawLines(pen, coords ))
window.MouseClick.Add mouseClick
I tried using the e.Location property that doesn't work which makes sense to some extend since when i print it then it prints "x=(some number) y=(some number)"
Can any one help me obtaining the x and y coordinate as an int?
As stated in the comment, to get mouse position from a MouseEventArgs you simply need to access it's X or Y properties
which just reflects the Location.X and Location.Y properties also available on e
Regarding your edit and your additional comment, I think you've done something wrong by adding a new Paint handler with each click and you just need to draw (which probably still requires a Refresh at some point though)
let mouseClick (e: MouseEventArgs) =
let x = e.X
let y = e.Y
let coords = [| System.Drawing.Point(x - 10, y - 10)
System.Drawing.Point(x - 10, y + 10)
System.Drawing.Point(x + 10, y + 10)
System.Drawing.Point(x + 10, y - 10)
System.Drawing.Point(x - 10, y - 10) |]
// maybe use instead of let ?
let g = window.CreateGraphics()
g.Graphics.DrawLines(pen, coords)
window.MouseClick.Add mouseClick

Appending to an empty array giving error

I have created an empty array to store data of the form (Int,Double). I gave up on trying to directly append a tuple to the array, as it seems swift is not set up to do this. So an example of my code looks like this:
var data: [(x: Int,y: Double)] = []
var newDataX: Int = 1
var newDataY: Double = 2.0
data.append(x: newDataX,y: newDataY)
The error message for the append line is "Type 'T' does not conform to protocol 'IntegerLiteralConvertible' which confuses me to no end. When I specifically append an integer and a double (ie. data.append(1,2.0)), I do not receive the error message. If I try to append one of the specifically and one of them using a variable I get the message no matter which one is the variable.
I would use the += command just append a tuple onto the array, but I the way I understand it, that is no longer a valid command in beta5. Is there something wrong with my code that I am not seeing? Or is there another way to do what I want to do?
The problem is that x: newDataX, y: newDataY is not resolved as a single parameter - instead it's passed as 2 separate parameters to the append function, and the compiler looks for a matching append function taking an Int and a Double.
You can solve the problem by defining the tuple before appending:
let newData = (x: 1, y: 2.0)
data.append(newData)
or making explicit that the parameters pair is a tuple. A tuple is identified by a list surrounded by parenthesis (1, 2.0), but unfortunately this doesn't work:
data.append( (x: 1, y: 2.0) )
If you really need/want to append in a single line, you can use a closure as follows:
data.append( { (x: newDataX, y: newDataY) }() )
A more elegant solution is by using a typealias:
typealias MyTuple = (x: Int, y: Double)
var data: [MyTuple] = []
var newDataX: Int = 1
var newDataY: Double = 2.0
data.append(MyTuple(x: 1, y: 2.0))

Resources