Difference between revisions of "ZZText"

From Second Life Wiki
Jump to navigation Jump to search
m (source tags)
 
(8 intermediate revisions by 5 users not shown)
Line 1: Line 1:
== What is it ==
== What is it ==


Scripting tools to allow display of text on a prim: [[XyText 1.5]] (aka XyText) , [[XyzzyText]], [[XyyyyzText]], [[XyzzyText-UTF8]], [[ZZText]]
Scripting tools to allow display of text on a prim: [[XyText 1.5]] (aka XyText) , [[XyzzyText]], [[XyyyyzText]], [[XyzzyText-UTF8]], [[ZZText]], [[VariText]]




Line 20: Line 20:
== How to generate the textures ==
== How to generate the textures ==


* You need to have GIMP 2.0 with python-fu extension installed
* GIMP 2.8 has already python-fu installed so you don't need to bother on installing it as I suggested some years ago
**Here the instructions on windows to do that:
** Install python 2.6.2 from http://www.python.org/download/,
** '''RESTART the PC!!'''
** Install gtk+ run time environment from: http://sourceforge.net/project/showfiles.php?group_id=71914
** '''''Install python extensions to graphic libs. Be sure to choose the 2.6.2 python compatibility'''''
*** Install pycairo from http://ftp.gnome.org/pub/GNOME/binaries/win32/pycairo/
*** Install pygobject from http://ftp.gnome.org/pub/GNOME/binaries/win32/pygobject/
*** Install pygtk from http://ftp.acc.umu.se/pub/GNOME/binaries/win32/pygtk/
** '''RESTART the PC!!'''
** Install gimp 2.6.6 from http://gimp.org/
** '''''Verify launching gimp, filters, python-fu, console to see Python 2.6.2'''''
* Use the mega-image maker python-fu script (see following sections)
* Use the mega-image maker python-fu script (see following sections)
* Use the splitter python-fu script to produce 512x512 guides for later splitting
* Use the splitter python-fu script to produce 512x512 guides for later splitting
Line 59: Line 48:
For not being so difficult to follow, I only publish differences between my script and [[XyText 1.5]] so that you might be able to understand better what I did:
For not being so difficult to follow, I only publish differences between my script and [[XyText 1.5]] so that you might be able to understand better what I did:
=== Change CHARACTER_GRID with our full permission textures ===
=== Change CHARACTER_GRID with our full permission textures ===
<lsl>
<source lang="lsl2">
list    CHARACTER_GRID  = [
list    CHARACTER_GRID  = [
         "96f4578b-879e-44ae-d223-427cc615f5a4", // my slice-0-0
         "96f4578b-879e-44ae-d223-427cc615f5a4", // my slice-0-0
Line 99: Line 88:
           ];
           ];


</lsl>
</source>


=== Enabling answering to only a specific channel instead of DISPLAY_STRING channel ===
=== Enabling answering to only a specific channel instead of DISPLAY_STRING channel ===
This heavily helps in producing multi cell boards:
This heavily helps in producing multi cell boards:
<lsl>
<source lang="lsl2">
integer ME;
integer ME;
....
default
state_entry(){
{
....
    state_entry()
ME=(integer)llGetObjectName(); // need that each cell has its own number as object name starting from 1000 for instance
    {
....
        // need that each cell has its own number as object name starting from 1000 for instance
// be sure we are answering only to channel ME
        ME = (integer)llGetObjectName();
link_message(integer sender, integer channel, string data, key id) {
    }
         if (channel == (ME) {
 
    // be sure we are answering only to channel ME
    link_message(integer sender, integer channel, string data, key id)
    {
         if (channel == (ME)
        {
             RenderString(data);
             RenderString(data);
             return;
             return;
         }
         }
</lsl>
    }
}
</source>


=== UTF-8 character specification ===
=== UTF-8 character specification ===
This must closely match what you produced with python-fu
This must closely match what you produced with python-fu
<lsl>
<source lang="lsl2">
ResetCharIndex() {
ResetCharIndex() {
     gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
     gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
Line 149: Line 145:
      
      
}
}
</lsl>
</source>


=== Accessing the new characters from textures ===
=== Accessing the new characters from textures ===
This is the most difficult part. Still need to clean up offsets, since now they are still a bit wrong.
This is the most difficult part. Still need to clean up offsets, since now they are still a bit wrong.


<lsl>
<source lang="lsl2">
vector GetGridOffset(vector grid_pos) {
vector GetGridOffset(vector grid_pos)
{
     // Zoom in on the texture showing our character pair.
     // Zoom in on the texture showing our character pair.
     integer Col = llRound(grid_pos.x) % 40; // PK was 20
     integer Col = llRound(grid_pos.x) % 40; // PK was 20
Line 164: Line 161:
}
}


ShowChars(vector grid_pos1, vector grid_pos2, vector grid_pos3, vector grid_pos4, vector grid_pos5) {
ShowChars(vector grid_pos1, vector grid_pos2, vector grid_pos3, vector grid_pos4, vector grid_pos5)
{
   // Set the primitive textures directly.
   // Set the primitive textures directly.
   
 
             
 
   llSetPrimitiveParams( [
   llSetLinkPrimitiveParamsFast(LINK_THIS, [
         PRIM_TEXTURE, FACE_1, GetGridTexture(grid_pos1), <0.125, 0.05, 0>, GetGridOffset(grid_pos1) + <0.0375-0.025-0.002, 0.025, 0>, 0.0,
         PRIM_TEXTURE, FACE_1, GetGridTexture(grid_pos1), <0.125, 0.05, 0>, GetGridOffset(grid_pos1) + <0.0375-0.025-0.002, 0.025, 0>, 0.0,
         PRIM_TEXTURE, FACE_2, GetGridTexture(grid_pos2), <0.05, 0.05, 0>, GetGridOffset(grid_pos2)+<-0.025-0.002, 0.025,0>, 0.0,
         PRIM_TEXTURE, FACE_2, GetGridTexture(grid_pos2), <0.05, 0.05, 0>, GetGridOffset(grid_pos2)+<-0.025-0.002, 0.025,0>, 0.0,
Line 174: Line 172:
         PRIM_TEXTURE, FACE_4, GetGridTexture(grid_pos4), <0.05, 0.05, 0>, GetGridOffset(grid_pos4)+<-0.025-0.002, 0.025,0>, 0.0,
         PRIM_TEXTURE, FACE_4, GetGridTexture(grid_pos4), <0.05, 0.05, 0>, GetGridOffset(grid_pos4)+<-0.025-0.002, 0.025,0>, 0.0,
         PRIM_TEXTURE, FACE_5, GetGridTexture(grid_pos5), <0.125, 0.05, 0>, GetGridOffset(grid_pos5) + <0.0375-0.025-0.077-0.002, 0.025, 0>, 0.0
         PRIM_TEXTURE, FACE_5, GetGridTexture(grid_pos5), <0.125, 0.05, 0>, GetGridOffset(grid_pos5) + <0.0375-0.025-0.077-0.002, 0.025, 0>, 0.0
//       PRIM_TEXTURE, FACE_1, GetGridTexture(grid_pos1), <0.25, 0.1, 0>, GetGridOffset(grid_pos1) + <0.075, 0, 0>, 0.0,
 
//       PRIM_TEXTURE, FACE_2, GetGridTexture(grid_pos2), <0.1, 0.1, 0>, GetGridOffset(grid_pos2), 0.0,
//     PRIM_TEXTURE, FACE_1, GetGridTexture(grid_pos1), <0.25, 0.1, 0>, GetGridOffset(grid_pos1) + <0.075, 0, 0>, 0.0,
//       PRIM_TEXTURE, FACE_3, GetGridTexture(grid_pos3), <-1.48, 0.1, 0>, GetGridOffset(grid_pos3)+ <0.37, 0, 0>, 0.0,
//     PRIM_TEXTURE, FACE_2, GetGridTexture(grid_pos2), <0.1, 0.1, 0>, GetGridOffset(grid_pos2), 0.0,
//       PRIM_TEXTURE, FACE_4, GetGridTexture(grid_pos4), <0.1, 0.1, 0>, GetGridOffset(grid_pos4), 0.0,
//     PRIM_TEXTURE, FACE_3, GetGridTexture(grid_pos3), <-1.48, 0.1, 0>, GetGridOffset(grid_pos3)+ <0.37, 0, 0>, 0.0,
//       PRIM_TEXTURE, FACE_5, GetGridTexture(grid_pos5), <0.25, 0.1, 0>, GetGridOffset(grid_pos5) - <0.075, 0, 0>, 0.0
//     PRIM_TEXTURE, FACE_4, GetGridTexture(grid_pos4), <0.1, 0.1, 0>, GetGridOffset(grid_pos4), 0.0,
       
//     PRIM_TEXTURE, FACE_5, GetGridTexture(grid_pos5), <0.25, 0.1, 0>, GetGridOffset(grid_pos5) - <0.075, 0, 0>, 0.0
         ]);
         ]);
}
}
 
integer GetIndex(string char)
integer GetIndex(string char)
{
{
     integer ret=llSubStringIndex(gCharIndex, char);
     integer ret = llSubStringIndex(gCharIndex, char);
     if(ret>=0) return ret;
 
   
     if(0 <= ret)
        return ret;
 
     // special char do nice trick :)
     // special char do nice trick :)
     string escaped=llEscapeURL(char);
     string escaped = llEscapeURL(char);
      
 
     if(escaped=="%E2%80%99") return 7; // remap ’
     // remap ’
     //llSay(0,"Looking for "+escaped);
     if (escaped == "%E2%80%99")
     integer found=llListFindList(decode, [escaped]);
        return 7;
   
 
     // llSay(PUBLIC_CHANNEL, "Looking for " + escaped);
     integer found = llListFindList(decode, [escaped]);
 
     // not found
     // not found
     if(found<0) return 0;
     if(found < 0)
   
        return FALSE;
 
     // return correct index
     // return correct index
     return llStringLength(gCharIndex)+found;
     return llStringLength(gCharIndex) + found;
   
}
}


RenderString(string str) {
RenderString(string str)
{
     // Get the grid positions for each pair of characters.
     // Get the grid positions for each pair of characters.
     vector GridPos1 = GetGridPos( GetIndex(llGetSubString(str, 0, 0)),
     vector GridPos1 = GetGridPos( GetIndex(llGetSubString(str, 0, 0)),
Line 219: Line 225:
     ShowChars(GridPos1, GridPos2, GridPos3, GridPos4, GridPos5);
     ShowChars(GridPos1, GridPos2, GridPos3, GridPos4, GridPos5);
}
}
 
</source>
</lsl>


== Splitter python-fu script ==
== Splitter python-fu script ==
Line 226: Line 231:


To avoid problems with indents, spaces and tabs create the file with explorer guides.py and right clich use the IDLE edit to past the following source.
To avoid problems with indents, spaces and tabs create the file with explorer guides.py and right clich use the IDLE edit to past the following source.
Note: you need to restart GIMP to see it working.


When running it is very simple just tell it "512" to split every 512 points.
When running it is very simple just tell it "512" to split every 512 points.
<python>
<source lang="python">
#!/usr/bin/env python
#!/usr/bin/env python


Line 236: Line 243:
def python_guides(timg, tdrawable, interval=100):
def python_guides(timg, tdrawable, interval=100):
     timg.undo_group_start()
     timg.undo_group_start()
     x=0
     for x in range(0, timg.width, interval):
    while x < timg.width:
       timg.add_vguide(x)
       timg.add_vguide(x)
      x+=interval
        
        
     y=0
     for y in range(0, timg.height, interval):
    while y < timg.height:
       timg.add_hguide(y)
       timg.add_hguide(y)
      y+=interval
        
        
     timg.undo_group_end()
     timg.undo_group_end()
Line 267: Line 270:
main()
main()


</python>
</source>
 
If the guides.py script is correctly installed you will see the following:
 
 
[[Image:XyText-utf8-Guides.jpg]]
 
Options to choose:
 
[[Image:XyText-UTF8-Guides2.jpg]]
 
And the image with the lines in blue:
 
[[Image:XyText-UTF8-Guides3.jpg]]


== Mega-image maker python-fu script ==
== Mega-image maker python-fu script ==
Line 278: Line 294:
If you need your characters you need to change the UTF-8 codings in "decode" var
If you need your characters you need to change the UTF-8 codings in "decode" var


<python>
<source lang="python">
#! /usr/bin/env python
#! /usr/bin/env python
from gimpfu import *
from gimpfu import *
Line 346: Line 362:
   for first in range(numtot):
   for first in range(numtot):
    
    
     if(first<maxchars):  
     if first<maxchars:  
         el1=chars[first]
         el1=chars[first]
     else:
     else:
Line 355: Line 371:


           # to save time removed function call
           # to save time removed function call
           if(second<maxchars):  
           if second<maxchars:  
             el2=chars[second]
             el2=chars[second]
           else:
           else:
Line 365: Line 381:
           pdb.gimp_text_fontname(img,layer,y+deltay,x,el2,0,TRUE,size,PIXELS,font)
           pdb.gimp_text_fontname(img,layer,y+deltay,x,el2,0,TRUE,size,PIXELS,font)
            
            
           index=index+1
           index+=1
            
            
    
    
Line 388: Line 404:


main()
main()
</python>
</source>
 
Here how the menu should appear:
 
[[Image:XyText-UTF8-Megaimagechoose.jpg]]
 
How to launch it: '''Note!! DON'T USE PROPORTIONAL CHARACTERS!!! BUT MONOSPACE 43 MIGHT BE BETTER'''
Also when checking use the limit font generation to 10: the computing process for all 200 characters is VEEERY LOOONG!!!! 0 means all the characters.
 
[[Image:XyText-UTF8-Megaimage2.jpg]]
 
How to zoom on the megaimage (Zoom 100%) and check produced characters:
 
[[Image:XyText-UTF8-Zoom11.jpg]]
 
As you see the default is producing BAD characters.. Proportional cannot be used since characters cannot be computed with xytext optimized algorithm.
 
[[Image:XyText-UTF8-Zoom2.jpg]]


== What if I want the full script? ==
== What if I want the full script? ==
Here you are:
Here you are:
<lsl>
<source lang="lsl2">
////////////////////////////////////////////
// ZZText (5 Face, Multi Texture)
// ZZText (5 Face, Multi Texture)
//
//
Line 407: Line 439:
// thane can use all the other commands
// thane can use all the other commands
// IDEAL for linked structures
// IDEAL for linked structures
////////////////////////////////////////////


/////////////// CONSTANTS ///////////////////
// XyText Message Map.
// XyText Message Map.
integer DISPLAY_STRING      = 0;
integer DISPLAY_STRING      = 0;
Line 432: Line 462:
integer FACE_5          = 1;
integer FACE_5          = 1;


list decode=[]; // to handle special characters from CP850 page for european countries
// to handle special characters from CP850 page for european countries
list decode = [];


// Used to hide the text after a fade-out.
// Used to hide the text after a fade-out.
key    TRANSPARENT    = "701917a8-d614-471f-13dd-5f4644e36e3c";
key    TRANSPARENT    = "701917a8-d614-471f-13dd-5f4644e36e3c";
// This is a list of textures for all 2-character combinations.
// This is a list of textures for all 2-character combinations.
list    CHARACTER_GRID  = [
list    CHARACTER_GRID  = [
Line 475: Line 507:
     "765cfd9c-9703-b5ba-2c5d-f1c8bd44e0f4"  // 7-7
     "765cfd9c-9703-b5ba-2c5d-f1c8bd44e0f4"  // 7-7
         ];
         ];
integer ME;
integer ME;
///////////// END CONSTANTS ////////////////


///////////// GLOBAL VARIABLES ///////////////
// All displayable characters.  Default to ASCII order.
// All displayable characters.  Default to ASCII order.
string gCharIndex;
string gCharIndex;
// This is the channel to listen on while acting
// This is the channel to listen on while acting
// as a cell in a larger display.
// as a cell in a larger display.
integer gCellChannel      = -1;
integer gCellChannel      = -1;
// This is the starting character position in the cell channel message
// This is the starting character position in the cell channel message
// to render.
// to render.
integer gCellCharPosition = 0;
integer gCellCharPosition = 0;
// This is whether or not to use the fade in/out special effect.
// This is whether or not to use the fade in/out special effect.
integer gCellUseFading      = FALSE;
integer gCellUseFading      = FALSE;
// This is how long to display the text before fading out (if using
// This is how long to display the text before fading out (if using
// fading special effect).
// fading special effect).
// Note: < 0  means don't fade out.
// Note: < 0  means don't fade out.
float  gCellHoldDelay      = 1.0;
float  gCellHoldDelay      = 1.0;
/////////// END GLOBAL VARIABLES ////////////


ResetCharIndex() {
ResetCharIndex()
{
     gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
     gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
     // \" <-- Fixes LSL syntax highlighting bug.
     // \" <-- Fixes LSL syntax highlighting bug.
Line 520: Line 555:
         //                  Sv          sv            zv            Zv              Y:            I:
         //                  Sv          sv            zv            Zv              Y:            I:
         "%C5%A0", "%C5%A1", "%C5%BE", "%C5%BD", "%C3%9D", "%C3%8C" ];
         "%C5%A0", "%C5%A1", "%C5%BE", "%C5%BD", "%C3%9D", "%C3%8C" ];
}
}


vector GetGridPos(integer index1, integer index2) {
vector GetGridPos(integer index1, integer index2)
{
     // There are two ways to use the lookup table...
     // There are two ways to use the lookup table...
     integer Col;
     integer Col;
     integer Row;
     integer Row;
     if (index1 >= index2) {
     if (index1 >= index2)
    {
         // In this case, the row is the index of the first character:
         // In this case, the row is the index of the first character:
         Row = index1;
         Row = index1;
Line 534: Line 569:
         Col = index2 * 2;
         Col = index2 * 2;
     }
     }
     else { // Index1 < Index2
     // Index1 < Index2
    else
    {
         // In this case, the row is the index of the second character:
         // In this case, the row is the index of the second character:
         Row = index2;
         Row = index2;
Line 540: Line 577:
         Col = index1 * 2 + 1;
         Col = index1 * 2 + 1;
     }
     }
     return <Col, Row, 0>;
     return <Col, Row, 0>;
}
}


string GetGridTexture(vector grid_pos) {
string GetGridTexture(vector grid_pos)
{
     // Calculate the texture in the grid to use.
     // Calculate the texture in the grid to use.
     integer GridCol = llRound(grid_pos.x) / 40; // PK was 20
     integer GridCol = llRound(grid_pos.x) / 40; // PK was 20
Line 553: Line 592:
}
}


vector GetGridOffset(vector grid_pos) {
vector GetGridOffset(vector grid_pos)
{
     // Zoom in on the texture showing our character pair.
     // Zoom in on the texture showing our character pair.
     integer Col = llRound(grid_pos.x) % 40; // PK was 20
     integer Col = llRound(grid_pos.x) % 40; // PK was 20
Line 562: Line 602:
}
}


ShowChars(vector grid_pos1, vector grid_pos2, vector grid_pos3, vector grid_pos4, vector grid_pos5) {
ShowChars(vector grid_pos1, vector grid_pos2, vector grid_pos3, vector grid_pos4, vector grid_pos5)
{
     // Set the primitive textures directly.
     // Set the primitive textures directly.




     llSetPrimitiveParams( [
     llSetLinkPrimitiveParamsFast(LINK_THIS, [
         PRIM_TEXTURE, FACE_1, GetGridTexture(grid_pos1), <0.125, 0.05, 0>, GetGridOffset(grid_pos1) + <0.0375-0.025-0.002, 0.025, 0>, 0.0,
         PRIM_TEXTURE, FACE_1, GetGridTexture(grid_pos1), <0.125, 0.05, 0>, GetGridOffset(grid_pos1) + <0.0375-0.025-0.002, 0.025, 0>, 0.0,
         PRIM_TEXTURE, FACE_2, GetGridTexture(grid_pos2), <0.05, 0.05, 0>, GetGridOffset(grid_pos2)+<-0.025-0.002, 0.025,0>, 0.0,
         PRIM_TEXTURE, FACE_2, GetGridTexture(grid_pos2), <0.05, 0.05, 0>, GetGridOffset(grid_pos2)+<-0.025-0.002, 0.025,0>, 0.0,
Line 572: Line 613:
         PRIM_TEXTURE, FACE_4, GetGridTexture(grid_pos4), <0.05, 0.05, 0>, GetGridOffset(grid_pos4)+<-0.025-0.002, 0.025,0>, 0.0,
         PRIM_TEXTURE, FACE_4, GetGridTexture(grid_pos4), <0.05, 0.05, 0>, GetGridOffset(grid_pos4)+<-0.025-0.002, 0.025,0>, 0.0,
         PRIM_TEXTURE, FACE_5, GetGridTexture(grid_pos5), <0.125, 0.05, 0>, GetGridOffset(grid_pos5) + <0.0375-0.025-0.077-0.002, 0.025, 0>, 0.0
         PRIM_TEXTURE, FACE_5, GetGridTexture(grid_pos5), <0.125, 0.05, 0>, GetGridOffset(grid_pos5) + <0.0375-0.025-0.077-0.002, 0.025, 0>, 0.0
            //       PRIM_TEXTURE, FACE_1, GetGridTexture(grid_pos1), <0.25, 0.1, 0>, GetGridOffset(grid_pos1) + <0.075, 0, 0>, 0.0,
//     PRIM_TEXTURE, FACE_1, GetGridTexture(grid_pos1), <0.25, 0.1, 0>, GetGridOffset(grid_pos1) + <0.075, 0, 0>, 0.0,
            //       PRIM_TEXTURE, FACE_2, GetGridTexture(grid_pos2), <0.1, 0.1, 0>, GetGridOffset(grid_pos2), 0.0,
//     PRIM_TEXTURE, FACE_2, GetGridTexture(grid_pos2), <0.1, 0.1, 0>, GetGridOffset(grid_pos2), 0.0,
            //       PRIM_TEXTURE, FACE_3, GetGridTexture(grid_pos3), <-1.48, 0.1, 0>, GetGridOffset(grid_pos3)+ <0.37, 0, 0>, 0.0,
//     PRIM_TEXTURE, FACE_3, GetGridTexture(grid_pos3), <-1.48, 0.1, 0>, GetGridOffset(grid_pos3)+ <0.37, 0, 0>, 0.0,
            //       PRIM_TEXTURE, FACE_4, GetGridTexture(grid_pos4), <0.1, 0.1, 0>, GetGridOffset(grid_pos4), 0.0,
//     PRIM_TEXTURE, FACE_4, GetGridTexture(grid_pos4), <0.1, 0.1, 0>, GetGridOffset(grid_pos4), 0.0,
            //       PRIM_TEXTURE, FACE_5, GetGridTexture(grid_pos5), <0.25, 0.1, 0>, GetGridOffset(grid_pos5) - <0.075, 0, 0>, 0.0
// PRIM_TEXTURE, FACE_5, GetGridTexture(grid_pos5), <0.25, 0.1, 0>, GetGridOffset(grid_pos5) - <0.075, 0, 0>, 0.0
 
             ]);
             ]);
}
}
Line 583: Line 623:
integer GetIndex(string char)
integer GetIndex(string char)
{
{
     integer ret=llSubStringIndex(gCharIndex, char);
     integer ret = llSubStringIndex(gCharIndex, char);
     if(ret>=0) return ret;
 
     if(0 <= ret)
        return ret;


     // special char do nice trick :)
     // special char do nice trick :)
     string escaped=llEscapeURL(char);
     string escaped = llEscapeURL(char);


     if(escaped=="%E2%80%99") return 7; // remap ’
    // remap ’
     //llSay(0,"Looking for "+escaped);
     if(escaped == "%E2%80%99")
     integer found=llListFindList(decode, [escaped]);
        return 7;
 
     //llSay(PUBLIC_CHANNEL, "Looking for " + escaped);
     integer found = llListFindList(decode, [escaped]);


     // not found
     // not found
     if(found<0) return 0;
     if(found < 0)
        return FALSE;


     // return correct index
     // return correct index
     return llStringLength(gCharIndex)+found;
     return llStringLength(gCharIndex) + found;


}
}


RenderString(string str) {
RenderString(string str)
{
     // Get the grid positions for each pair of characters.
     // Get the grid positions for each pair of characters.
     vector GridPos1 = GetGridPos( GetIndex(llGetSubString(str, 0, 0)),
     vector GridPos1 = GetGridPos( GetIndex(llGetSubString(str, 0, 0)),
Line 618: Line 665:
}
}


RenderWithEffects(string str) {
RenderWithEffects(string str)
{
     // Get the grid positions for each pair of characters.
     // Get the grid positions for each pair of characters.
     vector GridPos1 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)),
     vector GridPos1 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)),
Line 640: Line 688:
     for (Alpha = 0.10; Alpha <= 1.0; Alpha += 0.05)
     for (Alpha = 0.10; Alpha <= 1.0; Alpha += 0.05)
         llSetAlpha(Alpha, ALL_SIDES);
         llSetAlpha(Alpha, ALL_SIDES);
     // See if we want to fade out as well.
     // See if we want to fade out as well.
     if (gCellHoldDelay < 0.0)
     if (gCellHoldDelay < 0.0)
         // No, bail out. (Just keep showing the string at full strength).
         // No, bail out. (Just keep showing the string at full strength).
         return;
         return;
     // Hold the text for a while.
     // Hold the text for a while.
     llSleep(gCellHoldDelay);
     llSleep(gCellHoldDelay);
     // Now fade out.
     // Now fade out.
     for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
     for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
         llSetAlpha(Alpha, ALL_SIDES);
         llSetAlpha(Alpha, ALL_SIDES);
     // Make the text transparent to fully hide it.
     // Make the text transparent to fully hide it.
     llSetTexture(TRANSPARENT, ALL_SIDES);
     llSetTexture(TRANSPARENT, ALL_SIDES);
}
}


RenderExtended(string str) {
RenderExtended(string str)
{
     // Look for escape sequences.
     // Look for escape sequences.
     list Parsed      = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
     list Parsed      = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
Line 668: Line 721:
     integer LastWasEscapeSequence = FALSE;
     integer LastWasEscapeSequence = FALSE;
     // Work from left to right.
     // Work from left to right.
     for (i = 0; i < ParsedLen && IndicesLeft > 0; i++) {
     for (i = 0; i < ParsedLen && IndicesLeft > 0; i++)
    {
         Token = llList2String(Parsed, i);
         Token = llList2String(Parsed, i);


         // If this is an escape sequence, just set the flag and move on.
         // If this is an escape sequence, just set the flag and move on.
         if (Token == ESCAPE_SEQUENCE) {
         if (Token == ESCAPE_SEQUENCE)
        {
             LastWasEscapeSequence = TRUE;
             LastWasEscapeSequence = TRUE;
         }
         }
         else { // Token != ESCAPE_SEQUENCE
         // Token != ESCAPE_SEQUENCE
        else
        {
             // Otherwise this is a normal token.  Check its length.
             // Otherwise this is a normal token.  Check its length.
             Clipped = FALSE;
             Clipped = FALSE;
             integer TokenLength = llStringLength(Token);
             integer TokenLength = llStringLength(Token);
             // Clip if necessary.
             // Clip if necessary.
             if (TokenLength > IndicesLeft) {
             if (TokenLength > IndicesLeft)
            {
                 Token = llGetSubString(Token, 0, IndicesLeft - 1);
                 Token = llGetSubString(Token, 0, IndicesLeft - 1);
                 TokenLength = llStringLength(Token);
                 TokenLength = llStringLength(Token);
Line 690: Line 749:


             // Was the previous token an escape sequence?
             // Was the previous token an escape sequence?
             if (LastWasEscapeSequence) {
             if (LastWasEscapeSequence)
            {
                 // Yes, the first character is an escape character, the rest are normal.
                 // Yes, the first character is an escape character, the rest are normal.


Line 701: Line 761:
                     Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
                     Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
             }
             }
             else { // Normal string.
             // Normal string.
            else
            {
                 // Just add the characters normally.
                 // Just add the characters normally.
                 integer j;
                 integer j;
Line 724: Line 786:
}
}


integer ConvertIndex(integer index) {
integer ConvertIndex(integer index)
{
     // This converts from an ASCII based index to our indexing scheme.
     // This converts from an ASCII based index to our indexing scheme.
     if (index >= 32) // ' ' or higher
    // ' ' or higher
     if (index >= 32)
         index -= 32;
         index -= 32;
     else { // index < 32
     // index < 32
    else
    {
         // Quick bounds check.
         // Quick bounds check.
         if (index > 15)
         if (index > 15)
Line 739: Line 805:
}
}


default {
default
     state_entry() {
{
     state_entry()
    {
         // Initialize the character index.
         // Initialize the character index.
         ResetCharIndex();
         ResetCharIndex();
Line 748: Line 816:
     }
     }


     link_message(integer sender, integer channel, string data, key id) {
     link_message(integer sender, integer channel, string data, key id)
         if (channel == (ME+DISPLAY_STRING)) {
    {
         if (channel == (ME+DISPLAY_STRING))
        {
             RenderString(data);
             RenderString(data);
             return;
             return;
         }
         }
         if (channel == (ME+DISPLAY_EXTENDED)) {
         if (channel == (ME+DISPLAY_EXTENDED))
        {
             RenderExtended(data);
             RenderExtended(data);
             return;
             return;
         }
         }
         if (channel == gCellChannel) {
         if (channel == gCellChannel)
        {
             // Extract the characters we are interested in, and use those to render.
             // Extract the characters we are interested in, and use those to render.
             string TextToRender = llGetSubString(data, gCellCharPosition, gCellCharPosition + 9);
             string TextToRender = llGetSubString(data, gCellCharPosition, gCellCharPosition + 9);
Line 766: Line 838:
             return;
             return;
         }
         }
         if (channel == (ME+REMAP_INDICES)) {
         if (channel == (ME+REMAP_INDICES))
        {
             // Parse the message, splitting it up into index values.
             // Parse the message, splitting it up into index values.
             list Parsed = llCSV2List(data);
             list Parsed = llCSV2List(data);
             integer i;
             integer i;
             // Go through the list and swap each pair of indices.
             // Go through the list and swap each pair of indices.
             for (i = 0; i < llGetListLength(Parsed); i += 2) {
             for (i = 0; i < llGetListLength(Parsed); i += 2)
            {
                 integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
                 integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
                 integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );
                 integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );
Line 787: Line 861:
             return;
             return;
         }
         }
         if (channel == (ME+RESET_INDICES)) {
         if (channel == (ME+RESET_INDICES))
        {
             // Restore the character index back to default settings.
             // Restore the character index back to default settings.
             ResetCharIndex();
             ResetCharIndex();
             return;
             return;
         }
         }
         if (channel == (ME+SET_CELL_INFO)) {
         if (channel == (ME+SET_CELL_INFO))
        {
             // Change the channel we listen to for cell commands, and the
             // Change the channel we listen to for cell commands, and the
             // starting character position to extract from.
             // starting character position to extract from.
Line 802: Line 878:
             return;
             return;
         }
         }
         if (channel == (ME+SET_THICKNESS)) {
         if (channel == (ME+SET_THICKNESS))
        {
             // Set our z scale to thickness, while staying fixed
             // Set our z scale to thickness, while staying fixed
             // in position relative the prim below us.
             // in position relative the prim below us.
Line 809: Line 886:
             // Reposition only if this isn't the root prim.
             // Reposition only if this isn't the root prim.
             integer ThisLink = llGetLinkNumber();
             integer ThisLink = llGetLinkNumber();
             if (ThisLink != 0 || ThisLink != 1) {
             if (ThisLink != 0 || ThisLink != 1)
            {
                 // This is not the root prim.
                 // This is not the root prim.
                 vector Up = llRot2Up(llGetLocalRot());
                 vector Up = llRot2Up(llGetLocalRot());
Line 821: Line 899:
             return;
             return;
         }
         }
         if (channel == (ME+SET_COLOR)) {
         if (channel == (ME+SET_COLOR))
        {
             vector newColor = (vector)data;
             vector newColor = (vector)data;
             llSetColor(newColor, ALL_SIDES);
             llSetColor(newColor, ALL_SIDES);
Line 827: Line 906:
     }
     }
}
}
 
</source>
</lsl>

Latest revision as of 13:48, 19 April 2016

What is it

Scripting tools to allow display of text on a prim: XyText 1.5 (aka XyText) , XyzzyText, XyyyyzText, XyzzyText-UTF8, ZZText, VariText


ZZText is a low-prim, low-lag, international variation of XyText with the following features:

  • can be enhanced with new fonts or UTF-8 characters using GIMP and python-fu.
  • uses only 35 textures instead of 66 for the 10 chars on prim solution (this gives a bit less quality then the original xytext, but under normal situations is acceptable.

Rationale: if you have problems with prim usage, then you can accept this compromise.

  • this particular combination of textures gives something way more rapid than XyzzyText, which is very slow for big boards, and less lagger than XyText.
  • small warning: there is still some slight improvements for this zzText to be reasonably acceptable (consider it in a Beta stage), will post soon when it will be fully acceptable.

Credits and copyright

ZZText is heavily dependent on XyText so it shares and extends credits and copyrights.

Also this is a low prim variation of the XyzzyText-UTF8 page but downgraded to use XyText lower quality paradigm instead than the 1 character face approach.

How to generate the textures

  • GIMP 2.8 has already python-fu installed so you don't need to bother on installing it as I suggested some years ago
  • Use the mega-image maker python-fu script (see following sections)
  • Use the splitter python-fu script to produce 512x512 guides for later splitting
  • Use the filter/web/slice... button to produce 64 slices in png format to retain transparence (from 0-0 to 7-7)
  • Manually remove the empty slices (the i-j where j>i for instance 2-3, 6-7) so you have only 35 images png.
  • Manually convert each png to tga (this is solving some problems with sl uploading regarding transparence)
  • Upload the textures keeping the order 0-0 1-0 1-1 2-0 2-1 2-3 .... until 7-7)


Manual parts can be automated by another python-fu, but I was so exhausted with the others that it was ok for me doing manually :)

There is still some fix needed to do on the python-fu script. Currently characters are still a bit unevenly put so that when displaying them depending on which couple using we might see some slight asymmetric unbalancing. You can note this only when looking very closely to the chars. Whenever I fix myself this problem I will post here.

For those of you who already accept fonts I did last week, look at next sections for the 35 texture UUIDs:

So how can I use it?

  • Put the script named ZZText into each prim of your board naming each cell 1000 ---- 1099 as far as you need cells
  • Have a main script doing the following: llMessageLinked(LINK_SET,1040,"çòàùè",NULL_KEY) to write on cell named 1040

To divide in rows, use simple modulus operations.

Variation on XyText 1.5 standard script

For not being so difficult to follow, I only publish differences between my script and XyText 1.5 so that you might be able to understand better what I did:

Change CHARACTER_GRID with our full permission textures

list    CHARACTER_GRID  = [
        "96f4578b-879e-44ae-d223-427cc615f5a4", // my slice-0-0
        "eab5360f-6653-593f-b679-69c68b0dd001", // 1-0
        "367330be-717a-277d-5205-131cd6ded458", // 1-1
        "32046675-6e7e-2425-ce77-69000b0b4d96", // 2-0
        "40085901-bde6-2dd4-40cd-b6d48d242997", // 2-1
        "205d318b-09b7-7ecc-922e-801c93a546c8", // 2-2
        "841e7826-3645-d4f0-d48b-389586dd8e90", // 3-0
        "e2db78c5-fb47-d767-4744-8cb6d84610d0", // 3-1
        "15312c89-afd4-854c-9d9b-5b9e11844aed", // 3-2
        "d180e771-0393-d09b-8cac-0af6d550ae4a", // 3-3
        "c6c1d2c8-5dfd-b13a-7f1e-c3ede8126769", // 4-0
        "bc100d0c-a445-947f-caa4-285d9cc8a9de", // 4-1
        "0b31e862-75a4-9ed3-6331-e9e700a0fedb", // 4-2
        "0eacc306-6bd5-ab31-de47-c686141a6733", // 4-3
        "b68de1d9-4890-74a0-4d25-d4d5c83a0dba", // 4-4
        "04b2bf9b-a8bb-0a39-1062-9b005229eba9", // 5-0
        "be293f10-25ec-beb9-738b-fe1892b82aef", // 5-1
        "5e9c8317-71f5-f073-76ab-7c412d3acb84", // 5-2
        "030b441b-9022-2aba-f7a7-af6810c354b8", // 5-3
        "9f455858-8ae6-3a9a-c2d4-bc5ee92430ac", // 5-4
        "ee741143-f01e-f730-667e-66a3cd57d1cc", // 5-5
        "710282bc-bd80-5a44-6987-0c4c11a4c294", // 6-0
        "c9c410db-675a-1e98-4107-debd3c73a754", // 6-1
        "486c2336-71c5-b962-266c-838a865c067c", // 6-2
        "d4722155-0b6d-3673-0208-a727729bc117", // 6-3
        "bc7af3cd-ee48-08df-fe8b-a6ca8a425733", // 6-4
        "f23d7a73-00c3-1ffd-69a7-0cca031c939d", // 6-5
        "aa95bfd1-ac80-f962-7acf-d6aeed9c7e81", // 6-6
        "53fd52e3-0e8b-dcc8-42f8-332aef421bd2", // 7-0
        "da3f6f09-3e3a-e156-8e2e-1040111a5635", // 7-1
        "409a95c0-b685-2036-d0de-75e8ab654243", // 7-2
        "14e05aca-3a61-b892-3632-dcde72324779", // 7-3
        "546c0e4f-b678-895d-2035-0f39bb7f4979", // 7-4
        "cf2fc337-109f-ba25-8ff5-786f7d6ceb5b", // 7-5
        "9943204b-a4de-868a-3cdd-8785d1864ede", // 7-6
        "765cfd9c-9703-b5ba-2c5d-f1c8bd44e0f4"  // 7-7
          ];

Enabling answering to only a specific channel instead of DISPLAY_STRING channel

This heavily helps in producing multi cell boards:

integer ME;
default
{
    state_entry()
    {
        // need that each cell has its own number as object name starting from 1000 for instance
        ME = (integer)llGetObjectName();
    }

    // be sure we are answering only to channel ME
    link_message(integer sender, integer channel, string data, key id)
    {
        if (channel == (ME)
        {
            RenderString(data);
            return;
        }
    }
}

UTF-8 character specification

This must closely match what you produced with python-fu

ResetCharIndex() {
    gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
    // \" <-- Fixes LSL syntax highlighting bug.
    gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
  //        cap cedille      u:         e/            a^         a:          a/         a ring      cedille     e^           e:
  decode=  ["%C3%87", "%C3%BC", "%C3%A9", "%C3%A2", "%C3%A4", "%C3%A0", "%C3%A5", "%C3%A7", "%C3%AA", "%C3%AB",
  
    
  //                    e\           i:               i^            i\                A:          A ring          E/              ae           AE           marker >
       "%C3%A8", "%C3%AF", "%C3%AE", "%C3%AC", "%C3%84", "%C3%85", "%C3%89", "%C3%A6", "%C3%86", "%E2%96%B6" ,

  //                 o:               o/           u^          u\              y:               O:             U:          cent           pound        yen
       "%C3%B6", "%C3%B2", "%C3%BB", "%C3%B9", "%C3%BF", "%C3%96", "%C3%9C", "%C2%A2", "%C2%A3", "%C2%A5",
   
  //                 A^              a/              i/                o/            u/              n~           E:            y/              inv ?         O^
       "%C3%82", "%C3%A1", "%C3%AD", "%C3%B3", "%C3%BA", "%C3%B1", "%C3%8B", "%C3%BD", "%C2%BF", "%C3%94",

  //                   inv !             I\             I/           degree       E^              I^            o^            U^
       "%C2%A1", "%C3%8C", "%C3%8D", "%C2%B0", "%C3%8A", "%C3%8E", "%C3%B4", "%C3%9B",

  //                     Y:          euro           german ss         E\              A\           A/              U\           U/               O\           O/
       "%C3%9D", "%E2%82%AC", "%C3%9F", "%C3%88", "%C3%80", "%C3%81", "%C3%99", "%C3%9A", "%C3%92", "%C3%93",

  //                   Sv           sv             zv             Zv              Y:             I:
       "%C5%A0", "%C5%A1", "%C5%BE", "%C5%BD", "%C3%9D", "%C3%8C" ];
     
    
}

Accessing the new characters from textures

This is the most difficult part. Still need to clean up offsets, since now they are still a bit wrong.

vector GetGridOffset(vector grid_pos)
{
    // Zoom in on the texture showing our character pair.
    integer Col = llRound(grid_pos.x) % 40; // PK was 20
    integer Row = llRound(grid_pos.y) % 20; // PK was 10

    // Return the offset in the texture.
    return <-0.45 + 0.025 * Col, 0.45 - 0.05 * Row, 0.0>; // PK was 0.05 and 0.1
}

ShowChars(vector grid_pos1, vector grid_pos2, vector grid_pos3, vector grid_pos4, vector grid_pos5)
{
   // Set the primitive textures directly.


   llSetLinkPrimitiveParamsFast(LINK_THIS, [
        PRIM_TEXTURE, FACE_1, GetGridTexture(grid_pos1), <0.125, 0.05, 0>, GetGridOffset(grid_pos1) + <0.0375-0.025-0.002, 0.025, 0>, 0.0,
        PRIM_TEXTURE, FACE_2, GetGridTexture(grid_pos2), <0.05, 0.05, 0>, GetGridOffset(grid_pos2)+<-0.025-0.002, 0.025,0>, 0.0,
        PRIM_TEXTURE, FACE_3, GetGridTexture(grid_pos3), <-0.74, 0.05, 0>, GetGridOffset(grid_pos3)+ <-.34-0.002, 0.025, 0>, 0.0,
        PRIM_TEXTURE, FACE_4, GetGridTexture(grid_pos4), <0.05, 0.05, 0>, GetGridOffset(grid_pos4)+<-0.025-0.002, 0.025,0>, 0.0,
        PRIM_TEXTURE, FACE_5, GetGridTexture(grid_pos5), <0.125, 0.05, 0>, GetGridOffset(grid_pos5) + <0.0375-0.025-0.077-0.002, 0.025, 0>, 0.0

//      PRIM_TEXTURE, FACE_1, GetGridTexture(grid_pos1), <0.25, 0.1, 0>, GetGridOffset(grid_pos1) + <0.075, 0, 0>, 0.0,
//      PRIM_TEXTURE, FACE_2, GetGridTexture(grid_pos2), <0.1, 0.1, 0>, GetGridOffset(grid_pos2), 0.0,
//      PRIM_TEXTURE, FACE_3, GetGridTexture(grid_pos3), <-1.48, 0.1, 0>, GetGridOffset(grid_pos3)+ <0.37, 0, 0>, 0.0,
//      PRIM_TEXTURE, FACE_4, GetGridTexture(grid_pos4), <0.1, 0.1, 0>, GetGridOffset(grid_pos4), 0.0,
//      PRIM_TEXTURE, FACE_5, GetGridTexture(grid_pos5), <0.25, 0.1, 0>, GetGridOffset(grid_pos5) - <0.075, 0, 0>, 0.0
 
        ]);
}
 
integer GetIndex(string char)
{
    integer ret = llSubStringIndex(gCharIndex, char);

    if(0 <= ret)
        return ret;

    // special char do nice trick :)
    string escaped = llEscapeURL(char);

    // remap ’
    if (escaped == "%E2%80%99")
        return 7;

    // llSay(PUBLIC_CHANNEL, "Looking for " + escaped);
    integer found = llListFindList(decode, [escaped]);

    // not found
    if(found < 0)
        return FALSE;

    // return correct index
    return llStringLength(gCharIndex) + found;
 
}

RenderString(string str)
{
    // Get the grid positions for each pair of characters.
    vector GridPos1 = GetGridPos( GetIndex(llGetSubString(str, 0, 0)),
                                  GetIndex(llGetSubString(str, 1, 1)) );
    vector GridPos2 = GetGridPos( GetIndex(llGetSubString(str, 2, 2)),
                                  GetIndex(llGetSubString(str, 3, 3)) );
    vector GridPos3 = GetGridPos( GetIndex(llGetSubString(str, 4, 4)),
                                  GetIndex(llGetSubString(str, 5, 5)) );
    vector GridPos4 = GetGridPos( GetIndex(llGetSubString(str, 6, 6)),
                                  GetIndex(llGetSubString(str, 7, 7)) );
    vector GridPos5 = GetGridPos( GetIndex(llGetSubString(str, 8, 8)),
                                  GetIndex(llGetSubString(str, 9, 9)) );                                   

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridPos1, GridPos2, GridPos3, GridPos4, GridPos5);
}

Splitter python-fu script

You need to put this under the %USERPROFILE%\.gimp-2.6\plug-ins folder (in my case: \Documents and Settings\salahzar.SALAHZAR-PC\.gimp-2.6\plug-ins).

To avoid problems with indents, spaces and tabs create the file with explorer guides.py and right clich use the IDLE edit to past the following source.

Note: you need to restart GIMP to see it working.

When running it is very simple just tell it "512" to split every 512 points.

#!/usr/bin/env python

import math
from gimpfu import *

def python_guides(timg, tdrawable, interval=100):
    timg.undo_group_start()
    for x in range(0, timg.width, interval):
       timg.add_vguide(x)
       
    for y in range(0, timg.height, interval):
       timg.add_hguide(y)
       
    timg.undo_group_end()
    
    gimp.displays_flush()

register(
        "python_fu_guides",
        "Guides: this will split the images in sections...",
        "Guides: this will split the images in sections...",
        "Salahzar Stenvaag",
        " ",
        "2008",
        "<Image>/Filters/Guides...",
        "RGB*, GRAY*",
        [
                (PF_INT, "interval", "Interval", 100)
        ],
        [],
        python_guides)

main()

If the guides.py script is correctly installed you will see the following:


XyText-utf8-Guides.jpg

Options to choose:

XyText-UTF8-Guides2.jpg

And the image with the lines in blue:

XyText-UTF8-Guides3.jpg

Mega-image maker python-fu script

You need to put this under the %USERPROFILE%\.gimp-2.6\plug-ins folder (in my case: \Documents and Settings\salahzar.SALAHZAR-PC\.gimp-2.6\plug-ins).

To avoid problems with indents, spaces and tabs create the file with explorer megaimages.py and right clich use the IDLE edit to past the following source.


If you need your characters you need to change the UTF-8 codings in "decode" var

#! /usr/bin/env python
from gimpfu import *

# you can remove logging, I used it to be sure program was working since it can be a bit slow 
# to generate all the combination
def python_log_init():
    fileHandle = open( 'python.log', 'w')
    fileHandle.close()
    
def python_log(s):
    fileHandle = open ( 'python.log', 'a' )
    fileHandle.write(str(s)+"\n")
    fileHandle.close() 

def python_xytext2(font,color,size,limit):
  """Print the arguments on standard output"""
  python_log_init()
  python_log("font: %s color: <%d,%d,%d> size: %d limit: %d" % ( font, color[0], color[1], color[2], size, limit ))
  chars = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
  
  
  # 
  
  #        cap cedille      u:         e/            a^         a:          a/         a ring      cedille     e^           e:
  decode=  ["\xC3\x87", "\xC3\xBC", "\xC3\xA9", "\xC3\xA2", "\xC3\xA4", "\xC3\xA0", "\xC3\xA5", "\xC3\xA7", "\xC3\xAA", "\xC3\xAB" ]  
  
    
  #                    e\           i:               i^            i\                A:          A ring          E/              ae           AE           marker >
  decode+=["\xC3\xA8", "\xC3\xAF", "\xC3\xAE", "\xC3\xAC", "\xC3\x84", "\xC3\x85", "\xC3\x89", "\xC3\xA6", "\xC3\x86", "\xE2\x96\xB6" ]

  #                 o:               o/           u^          u\              y:               O:             U:          cent           pound        yen
  decode+=["\xC3\xB6", "\xC3\xB2", "\xC3\xBB", "\xC3\xB9", "\xC3\xBF", "\xC3\x96", "\xC3\x9C", "\xC2\xA2", "\xC2\xA3", "\xC2\xA5"]
   
  #                 A^              a/              i/                o/            u/              n~           E:            y/              inv ?         O^
  decode+=["\xC3\x82", "\xC3\xA1", "\xC3\xAD", "\xC3\xB3", "\xC3\xBA", "\xC3\xB1", "\xC3\x8B", "\xC3\xBD", "\xC2\xBF", "\xC3\x94" ] 

  #                   inv !             I\             I/           degree       E^              I^            o^            U^
  decode+=["\xC2\xA1", "\xC3\x8C", "\xC3\x8D", "\xC2\xB0", "\xC3\x8A", "\xC3\x8E", "\xC3\xB4", "\xC3\x9B" ]

  #                     Y:          euro           german ss         E\              A\           A/              U\           U/               O\           O/
  decode+=["\xC3\x9D", "\xE2\x82\xAC", "\xC3\x9F", "\xC3\x88", "\xC3\x80", "\xC3\x81", "\xC3\x99", "\xC3\x9A", "\xC3\x92", "\xC3\x93"   ]                  

  #                   Sv           sv             zv             Zv              Y:             I:
  decode+=[ "\xC5\xA0", "\xC5\xA1", "\xC5\xBE", "\xC5\xBD", "\xC3\x9D", "\xC3\x8C" ]
 
 
  width=5120
  height=5120
  img = gimp.Image(width, height, RGB)
  layer = gimp.Layer(img, "my font", width, height, RGB_IMAGE, 100, NORMAL_MODE)
  img.add_layer(layer, 0)
  layer.add_alpha()
  gimp.set_foreground(color)
  pdb.gimp_selection_all(img)
  pdb.gimp_edit_clear(layer)
  pdb.gimp_selection_none(img)
  size= 20 # 23 # 21.3 # 30
  
  index=0
  numtot=len(chars)+len(decode)
  #numtot=50
  if limit>0: numtot=limit
  deltay=12.8 # 13.8215 # 12.8 # 18
  deltax=25.6 # 27.6432 # 25.6 # 36
  maxchars=len(chars)
  for first in range(numtot):
  
     if first<maxchars: 
        el1=chars[first]
     else:
        el1=decode[first-maxchars]
     
     python_log(str(first)+"/"+str(numtot)+":  "+el1)
     for second in range(first+1):

          # to save time removed function call
          if second<maxchars: 
             el2=chars[second]
          else:
             el2=decode[second-maxchars]
                 
          y=second * deltay * 2 # horizontal distance
          x=first*deltax # line distance
          pdb.gimp_text_fontname(img,layer,y,x,el1,0,TRUE,size,PIXELS,font)
          pdb.gimp_text_fontname(img,layer,y+deltay,x,el2,0,TRUE,size,PIXELS,font)
          
          index+=1
          
  
  # Now ready to display this image
  img.merge_visible_layers(0)
  gimp.Display(img)
   

register(
  "xytext2", "", "", "", "", "",
  "<Toolbox>/Xtns/_MegaImage", "",
  [
  (PF_FONT, "font", "Font to use", "Arial"),
  (PF_COLOR,"color","Color to use", (255,255,255) ),
  (PF_INT,    "size", "Font size", 45          ),
  (PF_INT,  "limit", "limit font generation ", 0  ),
 
  ],
  [],
  python_xytext2
  )

main()

Here how the menu should appear:

XyText-UTF8-Megaimagechoose.jpg

How to launch it: Note!! DON'T USE PROPORTIONAL CHARACTERS!!! BUT MONOSPACE 43 MIGHT BE BETTER Also when checking use the limit font generation to 10: the computing process for all 200 characters is VEEERY LOOONG!!!! 0 means all the characters.

XyText-UTF8-Megaimage2.jpg

How to zoom on the megaimage (Zoom 100%) and check produced characters:

XyText-UTF8-Zoom11.jpg

As you see the default is producing BAD characters.. Proportional cannot be used since characters cannot be computed with xytext optimized algorithm.

XyText-UTF8-Zoom2.jpg

What if I want the full script?

Here you are:

// ZZText (5 Face, Multi Texture)
//
// Originally Written by Xylor Baysklef
//
// Modified by Thraxis Epsilon January 20, 2006
// Added Support for 5 Face Prim, based on modification
// of XyText v1.1.1 by Kermitt Quick for Single Texture.
//
// Modified by Salahzar Stenvaag for International and new textures
// produced with GIMP. See wiki page ZZText for further info
// Obtain ME (starting linking channel from Object name)
// thane can use all the other commands
// IDEAL for linked structures

// XyText Message Map.
integer DISPLAY_STRING      = 0;
integer DISPLAY_EXTENDED    = 100;
integer REMAP_INDICES       = 200;
integer RESET_INDICES       = 300;
integer SET_CELL_INFO       = 400;
integer SET_THICKNESS       = 600;
integer SET_COLOR           = 700;

// This is an extended character escape sequence.
string  ESCAPE_SEQUENCE = "\\e";

// This is used to get an index for the extended character.
string  EXTENDED_INDEX  = "123456789abcdef";

// Face numbers.
integer FACE_1          = 3;
integer FACE_2          = 7;
integer FACE_3          = 4;
integer FACE_4          = 6;
integer FACE_5          = 1;

// to handle special characters from CP850 page for european countries
list decode = [];

// Used to hide the text after a fade-out.
key     TRANSPARENT     = "701917a8-d614-471f-13dd-5f4644e36e3c";

// This is a list of textures for all 2-character combinations.
list    CHARACTER_GRID  = [
    "96f4578b-879e-44ae-d223-427cc615f5a4", // my slice-0-0
    "eab5360f-6653-593f-b679-69c68b0dd001", // 1-0
    "367330be-717a-277d-5205-131cd6ded458", // 1-1
    "32046675-6e7e-2425-ce77-69000b0b4d96", // 2-0
    "40085901-bde6-2dd4-40cd-b6d48d242997", // 2-1
    "205d318b-09b7-7ecc-922e-801c93a546c8", // 2-2
    "841e7826-3645-d4f0-d48b-389586dd8e90", // 3-0
    "e2db78c5-fb47-d767-4744-8cb6d84610d0", // 3-1
    "15312c89-afd4-854c-9d9b-5b9e11844aed", // 3-2
    "d180e771-0393-d09b-8cac-0af6d550ae4a", // 3-3
    "c6c1d2c8-5dfd-b13a-7f1e-c3ede8126769", // 4-0
    "bc100d0c-a445-947f-caa4-285d9cc8a9de", // 4-1
    "0b31e862-75a4-9ed3-6331-e9e700a0fedb", // 4-2
    "0eacc306-6bd5-ab31-de47-c686141a6733", // 4-3
    "b68de1d9-4890-74a0-4d25-d4d5c83a0dba", // 4-4
    "04b2bf9b-a8bb-0a39-1062-9b005229eba9", // 5-0
    "be293f10-25ec-beb9-738b-fe1892b82aef", // 5-1
    "5e9c8317-71f5-f073-76ab-7c412d3acb84", // 5-2
    "030b441b-9022-2aba-f7a7-af6810c354b8", // 5-3
    "9f455858-8ae6-3a9a-c2d4-bc5ee92430ac", // 5-4
    "ee741143-f01e-f730-667e-66a3cd57d1cc", // 5-5
    "710282bc-bd80-5a44-6987-0c4c11a4c294", // 6-0
    "c9c410db-675a-1e98-4107-debd3c73a754", // 6-1
    "486c2336-71c5-b962-266c-838a865c067c", // 6-2
    "d4722155-0b6d-3673-0208-a727729bc117", // 6-3
    "bc7af3cd-ee48-08df-fe8b-a6ca8a425733", // 6-4
    "f23d7a73-00c3-1ffd-69a7-0cca031c939d", // 6-5
    "aa95bfd1-ac80-f962-7acf-d6aeed9c7e81", // 6-6
    "53fd52e3-0e8b-dcc8-42f8-332aef421bd2", // 7-0
    "da3f6f09-3e3a-e156-8e2e-1040111a5635", // 7-1
    "409a95c0-b685-2036-d0de-75e8ab654243", // 7-2
    "14e05aca-3a61-b892-3632-dcde72324779", // 7-3
    "546c0e4f-b678-895d-2035-0f39bb7f4979", // 7-4
    "cf2fc337-109f-ba25-8ff5-786f7d6ceb5b", // 7-5
    "9943204b-a4de-868a-3cdd-8785d1864ede", // 7-6
    "765cfd9c-9703-b5ba-2c5d-f1c8bd44e0f4"  // 7-7
        ];

integer ME;

// All displayable characters.  Default to ASCII order.
string gCharIndex;

// This is the channel to listen on while acting
// as a cell in a larger display.
integer gCellChannel      = -1;

// This is the starting character position in the cell channel message
// to render.
integer gCellCharPosition = 0;

// This is whether or not to use the fade in/out special effect.
integer gCellUseFading      = FALSE;

// This is how long to display the text before fading out (if using
// fading special effect).
// Note: < 0  means don't fade out.
float   gCellHoldDelay      = 1.0;

ResetCharIndex()
{
    gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
    // \" <-- Fixes LSL syntax highlighting bug.
    gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~";
    //        cap cedille      u:         e/            a^         a:          a/         a ring      cedille     e^           e:
    decode=  ["%C3%87", "%C3%BC", "%C3%A9", "%C3%A2", "%C3%A4", "%C3%A0", "%C3%A5", "%C3%A7", "%C3%AA", "%C3%AB",


        //                    e\           i:               i^            i\                A:          A ring          E/              ae           AE           marker >
        "%C3%A8", "%C3%AF", "%C3%AE", "%C3%AC", "%C3%84", "%C3%85", "%C3%89", "%C3%A6", "%C3%86", "%E2%96%B6" ,

        //                 o:               o/           u^          u\              y:               O:             U:          cent           pound        yen
        "%C3%B6", "%C3%B2", "%C3%BB", "%C3%B9", "%C3%BF", "%C3%96", "%C3%9C", "%C2%A2", "%C2%A3", "%C2%A5",

        //                 A^              a/              i/                o/            u/              n~           E:            y/              inv ?         O^
        "%C3%82", "%C3%A1", "%C3%AD", "%C3%B3", "%C3%BA", "%C3%B1", "%C3%8B", "%C3%BD", "%C2%BF", "%C3%94",

        //                   inv !             I\             I/           degree       E^              I^            o^            U^
        "%C2%A1", "%C3%8C", "%C3%8D", "%C2%B0", "%C3%8A", "%C3%8E", "%C3%B4", "%C3%9B",

        //                     Y:          euro           german ss         E\              A\           A/              U\           U/               O\           O/
        "%C3%9D", "%E2%82%AC", "%C3%9F", "%C3%88", "%C3%80", "%C3%81", "%C3%99", "%C3%9A", "%C3%92", "%C3%93",

        //                   Sv           sv             zv             Zv              Y:             I:
        "%C5%A0", "%C5%A1", "%C5%BE", "%C5%BD", "%C3%9D", "%C3%8C" ];
}

vector GetGridPos(integer index1, integer index2)
{
    // There are two ways to use the lookup table...
    integer Col;
    integer Row;
    if (index1 >= index2)
    {
        // In this case, the row is the index of the first character:
        Row = index1;
        // And the col is the index of the second character (x2)
        Col = index2 * 2;
    }
    // Index1 < Index2
    else
    {
        // In this case, the row is the index of the second character:
        Row = index2;
        // And the col is the index of the first character, x2, offset by 1.
        Col = index1 * 2 + 1;
    }

    return <Col, Row, 0>;
}

string GetGridTexture(vector grid_pos)
{
    // Calculate the texture in the grid to use.
    integer GridCol = llRound(grid_pos.x) / 40; // PK was 20
    integer GridRow = llRound(grid_pos.y) / 20; // PK was 10

    // Lookup the texture.
    key Texture = llList2Key(CHARACTER_GRID, GridRow * (GridRow + 1) / 2 + GridCol);
    return Texture;
}

vector GetGridOffset(vector grid_pos)
{
    // Zoom in on the texture showing our character pair.
    integer Col = llRound(grid_pos.x) % 40; // PK was 20
    integer Row = llRound(grid_pos.y) % 20; // PK was 10

    // Return the offset in the texture.
    return <-0.45 + 0.025 * Col, 0.45 - 0.05 * Row, 0.0>; // PK was 0.05 and 0.1
}

ShowChars(vector grid_pos1, vector grid_pos2, vector grid_pos3, vector grid_pos4, vector grid_pos5)
{
    // Set the primitive textures directly.


    llSetLinkPrimitiveParamsFast(LINK_THIS, [
        PRIM_TEXTURE, FACE_1, GetGridTexture(grid_pos1), <0.125, 0.05, 0>, GetGridOffset(grid_pos1) + <0.0375-0.025-0.002, 0.025, 0>, 0.0,
        PRIM_TEXTURE, FACE_2, GetGridTexture(grid_pos2), <0.05, 0.05, 0>, GetGridOffset(grid_pos2)+<-0.025-0.002, 0.025,0>, 0.0,
        PRIM_TEXTURE, FACE_3, GetGridTexture(grid_pos3), <-0.74, 0.05, 0>, GetGridOffset(grid_pos3)+ <-.34-0.002, 0.025, 0>, 0.0,
        PRIM_TEXTURE, FACE_4, GetGridTexture(grid_pos4), <0.05, 0.05, 0>, GetGridOffset(grid_pos4)+<-0.025-0.002, 0.025,0>, 0.0,
        PRIM_TEXTURE, FACE_5, GetGridTexture(grid_pos5), <0.125, 0.05, 0>, GetGridOffset(grid_pos5) + <0.0375-0.025-0.077-0.002, 0.025, 0>, 0.0
//      PRIM_TEXTURE, FACE_1, GetGridTexture(grid_pos1), <0.25, 0.1, 0>, GetGridOffset(grid_pos1) + <0.075, 0, 0>, 0.0,
//      PRIM_TEXTURE, FACE_2, GetGridTexture(grid_pos2), <0.1, 0.1, 0>, GetGridOffset(grid_pos2), 0.0,
//      PRIM_TEXTURE, FACE_3, GetGridTexture(grid_pos3), <-1.48, 0.1, 0>, GetGridOffset(grid_pos3)+ <0.37, 0, 0>, 0.0,
//      PRIM_TEXTURE, FACE_4, GetGridTexture(grid_pos4), <0.1, 0.1, 0>, GetGridOffset(grid_pos4), 0.0,
//  PRIM_TEXTURE, FACE_5, GetGridTexture(grid_pos5), <0.25, 0.1, 0>, GetGridOffset(grid_pos5) - <0.075, 0, 0>, 0.0
            ]);
}

integer GetIndex(string char)
{
    integer ret = llSubStringIndex(gCharIndex, char);

    if(0 <= ret)
        return ret;

    // special char do nice trick :)
    string escaped = llEscapeURL(char);

    // remap ’
    if(escaped == "%E2%80%99")
        return 7;

    //llSay(PUBLIC_CHANNEL, "Looking for " + escaped);
    integer found = llListFindList(decode, [escaped]);

    // not found
    if(found < 0)
        return FALSE;

    // return correct index
    return llStringLength(gCharIndex) + found;

}

RenderString(string str)
{
    // Get the grid positions for each pair of characters.
    vector GridPos1 = GetGridPos( GetIndex(llGetSubString(str, 0, 0)),
        GetIndex(llGetSubString(str, 1, 1)) );
    vector GridPos2 = GetGridPos( GetIndex(llGetSubString(str, 2, 2)),
        GetIndex(llGetSubString(str, 3, 3)) );
    vector GridPos3 = GetGridPos( GetIndex(llGetSubString(str, 4, 4)),
        GetIndex(llGetSubString(str, 5, 5)) );
    vector GridPos4 = GetGridPos( GetIndex(llGetSubString(str, 6, 6)),
        GetIndex(llGetSubString(str, 7, 7)) );
    vector GridPos5 = GetGridPos( GetIndex(llGetSubString(str, 8, 8)),
        GetIndex(llGetSubString(str, 9, 9)) );

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridPos1, GridPos2, GridPos3, GridPos4, GridPos5);
}

RenderWithEffects(string str)
{
    // Get the grid positions for each pair of characters.
    vector GridPos1 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 0, 0)),
        llSubStringIndex(gCharIndex, llGetSubString(str, 1, 1)) );
    vector GridPos2 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 2, 2)),
        llSubStringIndex(gCharIndex, llGetSubString(str, 3, 3)) );
    vector GridPos3 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 4, 4)),
        llSubStringIndex(gCharIndex, llGetSubString(str, 5, 5)) );
    vector GridPos4 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 6, 6)),
        llSubStringIndex(gCharIndex, llGetSubString(str, 7, 7)) );
    vector GridPos5 = GetGridPos( llSubStringIndex(gCharIndex, llGetSubString(str, 8, 8)),
        llSubStringIndex(gCharIndex, llGetSubString(str, 9, 9)) );

    // First set the alpha to the lowest possible.
    llSetAlpha(0.05, ALL_SIDES);

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridPos1, GridPos2, GridPos3, GridPos4, GridPos5);

    float Alpha;
    for (Alpha = 0.10; Alpha <= 1.0; Alpha += 0.05)
        llSetAlpha(Alpha, ALL_SIDES);

    // See if we want to fade out as well.
    if (gCellHoldDelay < 0.0)
        // No, bail out. (Just keep showing the string at full strength).
        return;

    // Hold the text for a while.
    llSleep(gCellHoldDelay);

    // Now fade out.
    for (Alpha = 0.95; Alpha >= 0.05; Alpha -= 0.05)
        llSetAlpha(Alpha, ALL_SIDES);

    // Make the text transparent to fully hide it.
    llSetTexture(TRANSPARENT, ALL_SIDES);
}

RenderExtended(string str)
{
    // Look for escape sequences.
    list Parsed       = llParseString2List(str, [], [ESCAPE_SEQUENCE]);
    integer ParsedLen = llGetListLength(Parsed);

    // Create a list of index values to work with.
    list Indices;
    // We start with room for 6 indices.
    integer IndicesLeft = 10;

    integer i;
    string Token;
    integer Clipped;
    integer LastWasEscapeSequence = FALSE;
    // Work from left to right.
    for (i = 0; i < ParsedLen && IndicesLeft > 0; i++)
    {
        Token = llList2String(Parsed, i);

        // If this is an escape sequence, just set the flag and move on.
        if (Token == ESCAPE_SEQUENCE)
        {
            LastWasEscapeSequence = TRUE;
        }
        // Token != ESCAPE_SEQUENCE
        else
        {
            // Otherwise this is a normal token.  Check its length.
            Clipped = FALSE;
            integer TokenLength = llStringLength(Token);

            // Clip if necessary.
            if (TokenLength > IndicesLeft)
            {
                Token = llGetSubString(Token, 0, IndicesLeft - 1);
                TokenLength = llStringLength(Token);
                IndicesLeft = 0;
                Clipped = TRUE;
            }
            else
                IndicesLeft -= TokenLength;

            // Was the previous token an escape sequence?
            if (LastWasEscapeSequence)
            {
                // Yes, the first character is an escape character, the rest are normal.

                // This is the extended character.
                Indices += [llSubStringIndex(EXTENDED_INDEX, llGetSubString(Token, 0, 0)) + 95];

                // These are the normal characters.
                integer j;
                for (j = 1; j < TokenLength; j++)
                    Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
            }
            // Normal string.
            else
            {
                // Just add the characters normally.
                integer j;
                for (j = 0; j < TokenLength; j++)
                    Indices += [llSubStringIndex(gCharIndex, llGetSubString(Token, j, j))];
            }

            // Unset this flag, since this was not an escape sequence.
            LastWasEscapeSequence = FALSE;
        }
    }

    // Use the indices to create grid positions.
    vector GridPos1 = GetGridPos( llList2Integer(Indices, 0), llList2Integer(Indices, 1) );
    vector GridPos2 = GetGridPos( llList2Integer(Indices, 2), llList2Integer(Indices, 3) );
    vector GridPos3 = GetGridPos( llList2Integer(Indices, 4), llList2Integer(Indices, 5) );
    vector GridPos4 = GetGridPos( llList2Integer(Indices, 6), llList2Integer(Indices, 7) );
    vector GridPos5 = GetGridPos( llList2Integer(Indices, 8), llList2Integer(Indices, 9) );

    // Use these grid positions to display the correct textures/offsets.
    ShowChars(GridPos1, GridPos2, GridPos3, GridPos4, GridPos5);
}

integer ConvertIndex(integer index)
{
    // This converts from an ASCII based index to our indexing scheme.
    // ' ' or higher
    if (index >= 32)
        index -= 32;
    // index < 32
    else
    {
        // Quick bounds check.
        if (index > 15)
            index = 15;

        index += 94; // extended characters
    }

    return index;
}

default
{
    state_entry()
    {
        // Initialize the character index.
        ResetCharIndex();
        ME=(integer)llGetObjectName();
        //llOwnerSay("Channel:"+(string)ME);
        //llSay(0, "Free Memory: " + (string) llGetFreeMemory());
    }

    link_message(integer sender, integer channel, string data, key id)
    {
        if (channel == (ME+DISPLAY_STRING))
        {
            RenderString(data);
            return;
        }
        if (channel == (ME+DISPLAY_EXTENDED))
        {
            RenderExtended(data);
            return;
        }
        if (channel == gCellChannel)
        {
            // Extract the characters we are interested in, and use those to render.
            string TextToRender = llGetSubString(data, gCellCharPosition, gCellCharPosition + 9);
            if (gCellUseFading)
                RenderWithEffects( TextToRender );
            else // !gCellUseFading
                RenderString( TextToRender );
            return;
        }
        if (channel == (ME+REMAP_INDICES))
        {
            // Parse the message, splitting it up into index values.
            list Parsed = llCSV2List(data);
            integer i;
            // Go through the list and swap each pair of indices.
            for (i = 0; i < llGetListLength(Parsed); i += 2)
            {
                integer Index1 = ConvertIndex( llList2Integer(Parsed, i) );
                integer Index2 = ConvertIndex( llList2Integer(Parsed, i + 1) );

                // Swap these index values.
                string Value1 = llGetSubString(gCharIndex, Index1, Index1);
                string Value2 = llGetSubString(gCharIndex, Index2, Index2);

                gCharIndex = llDeleteSubString(gCharIndex, Index1, Index1);
                gCharIndex = llInsertString(gCharIndex, Index1, Value2);

                gCharIndex = llDeleteSubString(gCharIndex, Index2, Index2);
                gCharIndex = llInsertString(gCharIndex, Index2, Value1);
            }
            return;
        }
        if (channel == (ME+RESET_INDICES))
        {
            // Restore the character index back to default settings.
            ResetCharIndex();
            return;
        }
        if (channel == (ME+SET_CELL_INFO))
        {
            // Change the channel we listen to for cell commands, and the
            // starting character position to extract from.
            list Parsed = llCSV2List(data);
            gCellChannel        = (integer) llList2String(Parsed, 0);
            gCellCharPosition   = (integer) llList2String(Parsed, 1);
            gCellUseFading      = (integer) llList2String(Parsed, 2);
            gCellHoldDelay      = (float)   llList2String(Parsed, 3);
            return;
        }
        if (channel == (ME+SET_THICKNESS))
        {
            // Set our z scale to thickness, while staying fixed
            // in position relative the prim below us.
            vector Scale    = llGetScale();
            float Thickness = (float) data;
            // Reposition only if this isn't the root prim.
            integer ThisLink = llGetLinkNumber();
            if (ThisLink != 0 || ThisLink != 1)
            {
                // This is not the root prim.
                vector Up = llRot2Up(llGetLocalRot());
                float DistanceToMove = Thickness / 2.0 - Scale.z / 2.0;
                vector Pos = llGetLocalPos();
                llSetPos(Pos + DistanceToMove * Up);
            }
            // Apply the new thickness.
            Scale.z = Thickness;
            llSetScale(Scale);
            return;
        }
        if (channel == (ME+SET_COLOR))
        {
            vector newColor = (vector)data;
            llSetColor(newColor, ALL_SIDES);
        }
    }
}