Detecting a mouse click / mouse up in NetLogo - user-interface

Using mouse-down? for mouse actions in NetLogo often results in the action happening too many times. For example, if you want to let users click to create new turtles, you could hook a forever button up to a procedure like:
to add-turtle
if mouse-down? [
crt 1 [ setxy mouse-xcor mouse-ycor ]
]
end
The problem is that this usually results in many turtles being created per click. I'd like to do something like:
to add-turtle
if mouse-clicked? [
crt 1 [ setxy mouse-xcor mouse-ycor ]
]
end
Where mouse-clicked? is true right as the person clicks (right after they left up the mouse button).

Unfortunately, you do have to keep track of it yourself, but the good news is that its not hard.
The key is to create a global called mouse-was-down? that is set to true if mouse-down? was true in the last time your mouse-oriented procedure was called. Then mouse-clicked? can be defined as follows:
to-report mouse-clicked?
report (mouse-was-down? = true and not mouse-down?)
end
It seems to work well in conjunction with a central mouse-management procedure that calls the other click-based procedures. For example:
globals [ mouse-was-down? ]
to-report mouse-clicked?
report (mouse-was-down? = true and not mouse-down?)
end
to mouse-manager
let mouse-is-down? mouse-down?
if mouse-clicked? [
add-turtle
; Other procedures that should be run on mouse-click
]
set mouse-was-down? mouse-is-down?
end
to add-turtle
crt 1 [ setxy mouse-xcor mouse-ycor ]
end
Using the mouse-is-down? local variable keeps the behavior more consistent if the mouse button is released before mouse-manager finishes.

Hello probably this is a bit late for your purpose, but in case anyone has the same problem, this is how I solved this in my models for now.
to add-turtle
if mouse-down? [
crt 1 [ setxy mouse-xcor mouse-ycor ]
stop
]
end
The workaround here is to add a "stop" command to kill the "create-new-turtle" forever button. The only tradeoff is that you will have to press again to create another new turtle.

Bryan Head's answer didn't work for me, it still created multiple turtles in one click.
Alternative:
A (non-forever) button which does crt 1 [ setxy mouse-xcor mouse-ycor ] with action key A.
Now all you need to do to add a turtle is press or hold A with your left hand while mousing with your right hand.

Here is an alternative to Bryan's code, in case you want to perform an action as soon as the mouse button is clicked (instead of waiting until it's released):
globals [ mouse-clicked? ]
to mouse-manager
ifelse mouse-down? [
if not mouse-clicked? [
set mouse-clicked? true
crt 1 [ setxy mouse-xcor mouse-ycor ]
]
] [
set mouse-clicked? false
]
end

Related

calculating with the values of links

I need to calculate a connection value between myself and all of my nearPersons which uses among others the trust value of the link. However, getting the trust values of all links and using these in the computation slows down the running time. I did not succeed to find another way to do it more efficiently. Any suggestions would be highly appreciated!
breed [persons person]
undirected-link-breed [connections connection]
connections-own [trust]
persons-own
[
nearPersons
familiarity
]
to setup
clear-all
setupPersons
setupConnections
updateConnections
reset-ticks
end
setupPersons
create-persons 1000
[
set color black
set grouped false
setxy random-xcor random-ycor
]
end
to setupConnections
ask persons [create-connections-with other persons]
ask connections [ set trust 0.4]
end
to updateConnections
while [(count persons with [grouped = false] / 1000) > 5]
[
let highlyTrusted n-of (2 + random 9) (persons with [grouped = false])
ask highlyTrusted
[
ask my-out-connections with [member? other-end highlyTrusted] [set trust 0.6]
set grouped true
]
]
end
to go
getNearPersons
calculateConnection
forward 1
end
to getNearPersons
ask persons [ set nearPersons other persons in-cone 3 360 ]
end
to calculateConnection
ask persons with [nearPersons != nobody]
[
ask nearPersons
[
let degreeOfTrust [trust] of in-connection-from myself
]
]
end
Here is the model using agentsets of trust rather than links. I think that it will give you identical results, as long as there are only those two trust levels. I made a few other changes in the code - in particular your original while statement would never run as it was written (the quotient was always 1). Most are simply matters of style. Let me know if this is not what you need. Still not a speed demon, about 5 seconds per tick with 1000 persons, but a lot faster than the link version. Note that nPersons is a global so that I could play around with it.
Charles
globals [nPersons]
breed [persons person]
persons-own
[
nearPersons
Trusted
unTrusted
familiarity
grouped
]
to setup
clear-all
set nPersons 1000
ask patches [ set pcolor gray ]
setupPersons
updateConnections
reset-ticks
end
to setupPersons
create-persons nPersons
[
set color black
set grouped false
setxy random-xcor random-ycor
]
ask persons [
set Trusted no-turtles
set unTrusted persons
set familiarity 1
]
end
to updateConnections
while [(count persons with [grouped = false] / nPersons) > 0.2]
[
let highlyTrusted n-of (2 + random 9) (persons)
; so persons can be in more than one trust group and treat all trust groups equally?
ask highlyTrusted
[
set Trusted other highlyTrusted
set grouped true
]
]
end
to go
ask persons [ getNearPersons ]
calculateConnection
ask persons [ forward 1 ]
tick
end
to getNearPersons
ask persons [ set nearPersons other persons in-radius 3 ]
end
to calculateConnection
ask persons with [any? nearPersons]
[
let affiliation []
ask nearPersons
[
let degreeOfTrust ifelse-value (member? myself Trusted) [0.6] [0.4]
; let degreeOfTrust [trust] of in-connection-from myself ;;this line causes netlogo to run very slowly
set affiliation lput (degreeOfTrust * familiarity) affiliation
]
]
end
I had to make a few modifications of your code to make it run, I've included it below. But I believe the real problem is just the scale of what you are doing. With 1000 persons, there are approximately half a million links that you are repeatedly polling, and given the size of your world, the number of nearPersons for each person is likely quite large. Do you really need 1000 persons in your model, and/or do you really need every person to be connected to every other person? The number of links goes up exponentially with the number of persons.
breed [persons person]
undirected-link-breed [connections connection]
connections-own [trust]
persons-own
[
nearPersons
familiarity
grouped
]
to setup
clear-all
setupPersons
show "persons setup"
setupConnections
show "connections setup"
updateConnections
show "connections updated"
reset-ticks
end
to setupPersons
create-persons nPersons
[
set color black
set grouped false
setxy random-xcor random-ycor
]
end
to setupConnections
ask persons [create-connections-with other persons]
ask connections [ set trust 0.4]
end
to updateConnections
while [(count persons with [grouped = false] / 1000) > 5]
[
let highlyTrusted n-of (2 + random 9) (persons)
ask highlyTrusted
[
ask my-out-connections with [member? other-end highlyTrusted] [set trust 0.6]
set grouped true
]
]
end
to go
ask persons [ getNearPersons ]
show mean [count nearPersons] of persons
ask persons [ calculateConnection ]
show "got Connections"
ask persons [ forward 1 ]
tick
end
to getNearPersons
ask persons [ set nearPersons other persons in-cone 3 360 ]
end
to calculateConnection
ask persons with [nearPersons != nobody]
[
let affiliation []
let idx 0
ask nearPersons
[
let degreeOfTrust [trust] of in-connection-from myself ;;this line causes netlogo to run very slowly
set affiliation insert-item idx affiliation (degreeOfTrust * familiarity)
set idx idx + 1
]
]
end

Initializing a matrix using a list with a string in it

Here is my code:
extensions [matrix]
..
sources-own[keyword1 keyword2 keyword3 keyword4 extrinsic-fitness visits]
..
to setup
create-sources
ask source 0 [
set keyword1 (matrix:from-row-list [["cat"][2]])
]
...
..
.
Now, when I click on SETUP and inspect "source 0", it shows the matrix to be initialized as the following:
{{matrix: [ [ 0 ][ 2 ] ]}}
Try as I might, I cannot get it to accept the string "cat" in place of the "0" in the first column.
OK, I got it.
A matrix in Netlogo can only hold numbers. One needs to use a "list" instead.

Using "myself" to change variables

I have 200 turtles who can be either sender or receiver of a message and are connected to each other in a random network. I am asking receivers to see if there are senders connected to them and if so, their knowledge value that is a set with 3 values regarding 3 items gets updated with knowledge value of the neighbor added to them.
the problem is myself can be easily used to be assigned to a variable, but in my case it cannot be used to change items in a pre-defined set as I encountered an error saying that this was not something I could use set on!
Any suggestions?
Thanks
Turtles-own [
knowledgeValue
receiver?
sender?
]
;Network is already constructed and turtles are connected.
to go
ask turtles [
set knowledgeValue []
]
ask turtles [
repeat 3 [
set knowledgeValue lput random-float 1 knowledgeValue
]
]
ask turtles with [receiver?] [
ask (turtles-on neighbors) with [sender?] [
; the knowledge of receiver gets updated
set [item 0 [knowledgeValue]] of myself [item 0 [knowledgeValue]] of myself + knowledgeValue
]
]
end
The problem is not myself. The problem is that you are trying to set a value in a list, whereas set is used for variables. Have a look at replace-item instead.
And you need to do something like ask myself set ....
If you must do it this way, here's one option. With this setup:
turtles-own [
knowledgeValue
receiver?
sender?
]
to setup
ca
resize-world -3 3 -3 3
set-patch-size 70
ask patches [
sprout 1 [
set receiver? false
set sender? false
ifelse random-float 1 > 0.5 [
set receiver? true
set color green
set knowledgeValue [0 0 0]
] [
set sender? true
set color red
set knowledgeValue []
repeat 3 [
set knowledgeValue lput random-float 1 knowledgeValue
]
]
]
]
reset-ticks
end
This generates a world where all receivers start with [0 0 0] as their knowledgeValue list (to prove that senders are the ones adding value). Now, have all receivers get their neighbors with sender? to ask the asking receiver to update that receiver's list. I don't really like this solution but it sort of approaches the issue from the same tack that you outlined.
to option-1
ask turtles with [ receiver? ] [
; ask senders to ask 'myself' (receiver)
ask ( turtles-on neighbors ) with [sender?] [
ask myself [
; note that the second myself (below) actually refers to the 'sender'
set knowledgeValue replace-item 0 knowledgeValue ( item 0 knowledgeValue + item 0 [knowledgeValue] of myself)
]
]
]
end
One alternative would be to simply sum the values needed and add them directly:
to option-2
ask turtles with [ receiver? ] [
let my-senders ( turtles-on neighbors ) with [ sender? ]
if any? my-senders [
; get the sum of their item 0 knowledgeValue
let to-add sum [ item 0 knowledgeValue ] of my-senders
; add it to the knowledgeValue
set knowledgeValue replace-item 0 knowledgeValue ( item 0 knowledgeValue + to-add )
]
]
end

Dynamic layout additions in Rebol3

I would like to dynamically add a button to the layout of a view, with the actor causing this addition belonging to a button that is already part of the layout.
I started with this:
REBOL [title: "Dynamic Button Addition"]
tilesize: 60x60
curtile: 1
stylize [
p: button [
facets: [init-size: tilesize max-size: tilesize]
actors: [
on-action: [
++ curtile
append tiles compose [ p (to-string curtile) ]
print ? tiles/options/content
v/redraw
]
]
]
]
v: [
tiles: hgroup [ p "1" ]
]
view v
...which does not appear to have the value of tiles/options/content change with each click.
I can get it to change if make these changes:
append tiledata compose [ p (to-string curtile) ]
and
tiledata: [ p "1" ]
v: [
tiles: hgroup tiledata
However, this does not cause any change on screen. If I replace the last four lines with this:
v: view [
tiles: hgroup tiledata
]
...so that v is now the view rather than the view's layout, I get this error when I click:
** Script error: v has no value
** Where: actor all foreach do-actor unless do-face if actor all foreach do-actor if do-event do-event if do-event either -apply- wake-up loop -apply- wait forever try do-events if view do either either either -apply-
** Near: actor face :data
This makes sense to me, because v is not yet done being defined, until I exit the program, IIUC.
How, then, can I make changes to v before the program ends, but after it's been passed to view?
Not very nice, but working if you replace
v/redraw
with these two lines
unview/all
view v
And there is a real dynamic example on how to update a layout that has already be viewed
I will simplify it
stylize [
tbox: hpanel [
actors: [
on-make: [
append face/options [
content: [
]
]
do-actor/style face 'on-make none 'hpanel
]
]
]
]
view/across [
button "button 1"
on-action [
append-content test compose [
button ( join "button " 2 + length? test/gob)
]
]
test: tbox
]

Evaluating code blocks in Rebol3

I'm trying to improve the Sliding Tile Puzzle example by making the starting positions random.
There's a better way to do this--"It is considered bad practice to convert values to strings and join them together to pass to do for evaluation."--but the approach I took was to try to generate Rebol3 source, and then evaluate it. I have it generating correctly, I think:
random/seed now
arr: random collect [ repeat tilenum 9 [ keep tilenum ] ]
hgroup-data: copy {}
repeat pos 9 [
curtile: (pick arr pos)
append hgroup-data either curtile = 9
[ reduce "x: box tilesize gameback " ]
[ rejoin [ { p "} curtile {" } ] ]
if all [(pos // 3) = 0 pos != 9] [ append hgroup-data " return^/" ]
]
print hgroup-data
...outputs something like:
p "4" x: box tilesize gameback p "5" return
p "3" p "7" p "1" return
p "2" p "8" p "6"
...which if I then copy and paste into this part, works correctly:
view/options [
hgroup [
PASTE-HERE
]
] [bg-color: gameback]
However, if I try to do it dynamically:
view/options [
hgroup [
hgroup-data
]
] [bg-color: gameback]
...(also print hgroup-data, do hgroup-data, and load hgroup-data), I get this error:
** GUI ERROR: Cannot parse the GUI dialect at: hgroup-data
...(or at: print hgroup-data, etc., depending on which variation I tried.)
If I try load [ hgroup-data ] I get:
** Script error: extend-face does not allow none! for its face argument
** Where: either if forever -apply- apply init-layout make-layout actor all foreach do-actor unless -apply- apply all build-face -apply- apply init-layout make-layout actor all foreach do-actor if build-face -apply- apply init-layout make-layout actor all foreach do-actor unless make-face -apply- apply case view do either either either -apply-
** Near: either all [
word? act: dial/1
block? body: get dial...
However, if I use the syntax hgroup do [ hgroup-data ], the program runs, but there are no buttons: it appears to be somehow over-evaluated, so that the return values of the functions p and box and so on are put straight into the hgroup as code.
Surely I'm missing an easy syntax error here. What is it?
First, I would say it's better to construct a block directly, instead of constructing a string and converting it to a block. But if you really want to do that, this should do the trick:
view/options compose/only [
hgroup (load hgroup-data)
] [bg-color: gameback]

Resources