|Did you know ...||Search Documentation:|
|Pack lps_corner -- swish/2dWord.md|
After executing a LPS program with
However, in some cases (mostly) fluents and (sometimes) events can have a "natural" representation in a two-dimensional space. In that case, by providing additional information - via a
display(Term,DisplaySpecification) predicate - the LPS programmer can also obtain an animation of fluents and events over time.
Consider the fluent
balance(Person,Amount). The following display/2 clause makes a fluent value appear as a green rectangle, with position depending on the person and height equal to the value:
display( balance(Person,V), [ from:[X,0], to:[RightX,V], label:(Person:V), type:rectangle, fontSize:13, fillColor:green ] ) :- (Person=bob,X=50;Person=fariba,X=200),RightX is X+70.
You can see this at work by trying banking example ; make sure you uncomment the
display(...) clauses if necessary.
Use the video controls to pause or step through the animation. Clicking the top left corner of the animation lets you see the timeline as usual; LPS timelines and 2d worlds are implemented as SWISH Prolog answer renderers.
In order to see your LPS program running in 2D, you need to write a few clauses for the display/2 predicate, specifying "display properties" for each fluent and/or event you wish to see.
NOTE: display/2 clauses must NOT use Prolog cuts nor if-then-elses, nor unbound arithmetic instructions - clauses should be calleable with all unbound head variables. Furthermore, display/2 must be deterministic when called with the first argument bound.
Next we look at what "props" (for short) are available.
So what can go into the second argument of
It can be a list of lists (each list defining one graphic object), or a simple list of the following properties:
Property | Possible values | Comment / Example ------------- | -------------| ---- type | rectangle circle line ellipse arc raster regularPolygon star path pointtext text arrow| Mandatory. All provided by PaperJS directly, except arrow id | Prolog term | optional unique identifier of the (displayed) term; the whole term will be taken as id by default label | Prolog term | the label is rendered in an arbitrary position; for precise positioning use a pointText/text object instead sendToBack | none| forces the object to be behind others bringToFront | none | draws the object in front scale | float number | scale:0.1 reduce 10x from | [X,Y] | a point, in pixels; the origin of coordinates is bottom left to | [X,Y] | point | [X,Y] | size | [X,Y] | radius | number | for type:circle, but also for type:rectangle (defines radius of round corners) radius1 | number | for type:star radius2 | number | for type:star points | integer | for type:star biDirectional | none | for type:arrow fillColor|any HTML 5 style color| fillColor:'#85bb65' strokeColor|ib.|strokeColor:black strokeWidth|integer|width of pen shadowColor|ib.| opacity|0..1.0|opacity:1.0 is the default; lower values increase transparency Others in paperjs|See http://paperjs.org/reference/path/|In general, any property accepted in a Path constructor will work
display(...) clauses can of course use Prolog predicates. The following is extracted from the burning example :
% Center a translucid star on the spot: display(ignite(X,Y), [ type:star,fillColor:red,center:[CX,CY],points:6,radius1:10,radius2:6,opacity:0.5 ]) :- pixels(X,Y,CX,CY). % convert our model coordinates to screen pixels pixels(X,Y,CX,CY) :- CX is X*20+10, CY is Y*20+10.
Some more notes:
Up to now we've been assuming that fluent and event values are situated in time, and display/2 transforms them into "renderable" props lists. But what if you want to have some constant background objects, which do not depend on time at all? You could simply create "dummy", constant fluents, and display these; but given how frequent this need is there's a simpler way: the
The following defines the (unchanging) background for the banking example:
display(timeless,[ % a display spec can be a list of properties (for one object) or a list of lists (4 objects here:) [type:star, center:[250,150], points:9, radius1:20, radius2:25, fillColor:yellow, sendToBack], [type:rectangle, from:[0,0], to:[320,200], sendToBack, fillColor:[0,0.746,1]], % R,G,B [type:ellipse, shadowOffset:5, shadowColor:darkGray , point:[50,150], size:[110, 40],fillColor: white], [type:ellipse, point:[20,130], size:[90, 30],fillColor: white ] ]).
Timeless component objects can have different ids, to allow for GUI event detection.
Alternatively, the LPS program can be executed in background with the
predicate. If the program contains display/2 clauses, a 2d view will appear in the query answers panel, displaying fluents and events incrementally (*or "lazily" as referred in the source code*) and in real time. Add a
maxRealTime(Seconds)` if you want to specify the time the program has to run (20 seconds being the default).
User clicks onto fluents and other objects (identified by their 'id' property - the whole object term by default) originate events:
lps_mouseup(ID,X,Y) lps_mousedown(ID,X,Y) lps_mousedrag(ID,X,Y)
You need to declare these as
`events` as usual. See examples
badlight\_user.pl`. X and Y are pixel coordinates in the whole scene.
Alt-clicking the scene pauses/resumes the LPS program execution, allowing the user (for example) to perform other clicks to appear as events in the next cycle. Notice that LPS cycle time is suspended, but not real time.