SVG Canvas: A way to generate dynamics graphics on prim faces (without MoaP)
under review
Jenna Felton
Motivation:
Second life is full of information and very often you have to display some information dynamically: Radars, navigation signs, fancy menus, information boards, HUD applications. And that is a rough listing.
For doing this we have prepared textures, MoaP, hover text. Textures can use a wast number of faces if you want to display an arbitrary text. MoaP is doable but has problems with transparency. And security concerns.
Hover text works well but it can not be limited to a certain area. Like not at all.
SVG Canvas tries to counter these issues and to give a way to create simple graphical information dynamically (but it will be much richer then a simple hover text).
Basic idea
A SVG canvas is a container for a SVG image and provider of its content: The container is created by a script, it has an own UUID which can be applied to any prim face like a regular texture. To display this image the viewer receives the SVG content from this container.
The SVG canvas is stored within the heap memory of the script created it (parent script). As long the script is available, the region can provide the SVG content to the interested viewer. Thus, the SVG canvas is best used on HUDs and in-world objects also running the parent script.
The canvas is created via the call
key llSVGCanvas(list options);
The options list defines the
<?xml>
header (the only way to set the <?xml>
attributes).The result key is used in the following commands and is also applied to prim faces.
To check the current status of the canvas we use the function
integer llSVGStatus(key canvas);
The function checks if the key is a SVG canvas, if it was created by the same script, if it is stil valid etc. Result is 0 or error code.
Since SVG is a XML dialect, to build/draw the SVG image we use DOM commands, but a non-OOP way. The commands can only be called within the parent script (for now).
integer llSVGGetElementByID(key canvas, string id);
This command finds the element having the given "id" attribute and returns its number. Each SVG element is addressed by a distinct number maintained internally, which can but must not be saved in the "id" attribute explicitly. For example we can use the order in which the elements were added while the top-level
<svg>
node has the element number 0.integer llSVGSetAttributes(key canvas, integer element, list attribs);
integer llSVGUpdateAttributes(key canvas, integer element, list attribs);
list llSVGGetAttributes(key canvas, integer element);
The commands set or read attributes of the SVG element given by the number. The attributes are given by name and value (alternating). The .Set. command replaces the element attributes, the .Update. command only changes values of named attributes. Both return a negative value on error and the (same) element number on success.
integer llSVGSetXML(key canvas, integer element, string xml);
string llSVGGetXML(key canvas, integer element);
This command sets and retrieves the XML content of the given element. This is used to change the text of the
<text>
element but also allows to draw the <svg>
image at once when desired this way.integer llSVGAddElement(key canvas, integer parent, integer sibling, string tag, list attribs, string XML);
This call creates a new SVG element with given tag, attributes, and content and places it is as child element of the given parent element
after
the element given by sibling when it is an element of the parent element. Otherwise the new element is placed after all child elements of the parent. The result is the number of the new element or negative on error. This operation may change numbers of some elements. The top-level <svg> node exists by default and must not be created.integer llSVGRemoveElment(key canvas, integer element);
This operation removes the SVG element having the given number. The top-level
<svg>
node can not be removed.Extensions
- Besides the simple elements (path, text etc.) we can also have commands for drawing of UI elements. The commands will provide only logical definition while the viewer will render the elements with respect of the current skin. This will allow to create apps with a look and feel of viewer floaters while they will operate on scripts running on the region.
- Since some of these elements must be clickable, the elements can be added proprietary attributes working like interaction listens and eg. on touch triggering the event
on_svg_event(key object, key canvas, integer element, integer action)
Within this event one could call then llDetected... functions for further information.
- Instead of creating the feature for SVG only we can first create the framework for building and updating a XML documents and use this to define SVG images among other uses.
Example follows in a comment.
Log In
Cutie Crush
Jenna Felton
Hello Cutie Crush :)
Yes, it is very related. In the linked post was a method suggested to render an arbitrary text over a surface. Which will be sufficient for many applications, but there was also an idea to use SVG which can allow dynamic (or temporary) graphics drawn on surfaces and it will allow even more applications.
So, the idea was to introduce a SVG canvas that holds the content of the image inside a script memory (i think it is the most optimal location) and provide a number of functions that manipulate this content in a simple way.
But both suggestions aim the the goal in the same direction, i think.
Cutie Crush
Jenna Felton There's definitely plenty of overlap between both feature requests, and if a project were planned to do one, it would probably be advantageous to incorporate 'doing both' at the same time.
And it would make for a great new 'feature package' with both those things together.
Spidey Linden
under review
Jenna Felton
The text needs an example but it was too long for canny, so, here is a comment :)
To define a simple SVG image, we could use this call set:
key canvas = llSVGCanvas([
"version", "1.0", "encoding", "UTF-8",
"standalone", "no"]);
if (llSVGStatus(canvas) < 0) return;
// Let's assume the rest will run ok.
// Element #0 is the top level svg node
llSVGSetAttributes(canvas, 0, [
"xmlns:svg", "http://www.w3.org/2000/svg",
"xmlns", "http://www.w3.org/2000/svg",
"version", "1.0",
"width", "220",
"height", "220"]);
// There is no nesting, the parent is top-level 0 and
// using -1 as sibling ads the elements one after one
integer rect = llSVGAddElement(canvas, 0, -1, "rect", [
"width", "66", "height", "30",
"x", "21", "y", "32",
"stroke", "#204a87",
"stroke-width", "2", "fill", "none"], "");
integer text = llSVGAddElement(canvas, 0, -1, "text", [
"x", "21", "y", "82"], "Rectangle");
This code will create an SVG image with this content (instead of calling
llSVGAddElement
function two times we can call llSVGSetXML
command once with the content of the <svg> node if we want so.):<?xml version="1.0"
encoding="UTF-8" standalone="no"?>
<svg xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.0" width="220" height="220">
<rect width="66" height="30" x="21" y="32"
stroke="#204a87" stroke-width="2" fill="none"/>
<text x="21" y="82">Rectangle</text>
</svg>
But if we'd generate the image by direct providing the XML content of the
<svg>
node, we'd need to give the nodes rect
and text
some distinct ID's and then ask their element numbers via the llSVGGetElementByID
function afterwards.Because now we can do this: Whenever we want, we can change the text of the text node:
llSVGSetXML(canvas, text, "click this");
And we also add a touch listen to the rectangle:
llSVGUpdateAttributes(canvas, rect, ["event", "SVG_TOUCH"]);
which is a proprietary attribute and will trigger an event on touch we can handle:
on_svg_event(key obj, key canv,
integer elem, integer action) {
if (action == SVG_TOUCH)
if (elem == rect)
if (canv == canvas)
{
llOwnerSay("thanks 4 clicking!");
}
}
Jenna Felton
Sorry for talking to myself :) Actually the topics is a bit complicated and the intention was to give an initial idea and then when it is as good as I think it is there will be discussions and final details will be talked out. The code was meant as initial suggestions. But some thinking lead to some suggestions :)
I think it makes sense to separate the SVG document and the top level
<svg>
node. The latter will be created automatically (to save one call) and receives the element number 1, while the the whole document the element number 0.This will allow an access to the whole document (by using the element number 0) and to the top-level
<svg>
note separately, by using element number 1.Access to the XML content of a node
including the tags
of the element (outer XML) via functionsstring llSVGGetXML(key canvas, integer element);
integer llSVGSetXML(key canvas, integer element,
string XML);
Access to the part between the element tags (inner XML) via functions
string llSVGGetContent(key canvas, integer element);
integer llSVGSetContent(key canvas, integer element,
string XML);
Access to attributes like above. To create the new nodes we'll need the functions
integer llSVGAddChild(key canvas, integer element,
integer append, string tag, list attribs,
string XML);
The function creates a new element node as a child node of the given element. The new node is placed after (append = TRUE) or before (append = FALSE) the other child nodes. Result is the number of the new element or error code.
integer llSVGAddSibling(key canvas, integer element,
integer append, string tag, list attribs,
string XML);
This function creates a new element and inserts it after (append = TRUE) or before (append = FALSE) the given element as child element of its parent, i.e. as a sibling element of the given one.
Removing an element via
llSVGRemoveElment
as explained. I think that must be enough to create SVG image even using CSS. So I stop adding suggestions now, and i am curious about other people suggestions :)
c
coldheartmonster Resident
Jenna Felton I find many interesting things that could be done (math, ML...), In SL however drawing or writing on a prim is something we some of us been waiting for 20 years. https://github.com/Martin-Pitt/NexiiText4 (accepts many formats JSON, XML, rrss ...) there are a few inventions just like xytext that permit functionalities. What I love of SL is that there's infinite ways to build. [in the image text displayed from a notecard low land impact]
Jenna Felton
Hi coldheartmonster Resident :)
Thank you for your reply :) Yes it is great that there are many ways to build things in SL and you can choose whatever it is more appropriate for a task you are doing.
Thank you also for linking NexiiText :) I have not figured out yet how to run it but with more time I will :) The project seems very interesting.