SungAli

Cooroutine Problem

Mar 5th, 2026
50
0
Never
4
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.40 KB | None | 0 0
  1. ---[=[ Name, Version, and Description management.
  2. _Name_ = `ZYX Euler Rotation (Gimbal) Demonstrator`
  3. _Version_ = "1.5"
  4. _Desc_ = [[
  5. Prototype teaching toy. Work in Progress.
  6. Feel Free to take a copy to play with.
  7.   ]]
  8. ll.SetObjectName( `{_Name_}: {_Version_}` ); ll.SetObjectDesc( _Desc_ )
  9. --]=]
  10.  
  11. function concat( ... ) -- Return stringized values as a comma separated sting. Avoid using tables so explicit nils are preserved.
  12.     local count = select("#", ...); if count == 0 then return "" end--if
  13.     local result = tostring(select(1, ...)) for i = 2, count do result = result .. `{", "}{tostring(select(i, ...))}` end--for
  14.     return result
  15. end--concat
  16.  
  17. local rawprint = print -- always enabled just in case some code is using rawprint to bypass the redirect below
  18. local function print(...) ll.Shout( DEBUG_CHANNEL, concat(...) )  end--print
  19.  
  20.  
  21. ---[[ Send DBG messages to the Debug Channel to sync with error messages and avoid spamming Nearby Chat
  22. local function rawDBG( msg ) ll.Shout( DEBUG_CHANNEL, msg ) end--rawDBG this function exist to allow redirecton of all DBG output to a single destination in one place
  23. local function DBG( ... ) local n,l = debug.info(2, "nl"); rawDBG( `{ll.GetTimestamp()}: {l} {n}: {concat(...)}` ); ll.Sleep(0.2) end--DBG  (with timestamp)
  24.  
  25. local function DBG_dump( label, val, lvl ) --dump any data structure
  26.     lvl = lvl or 0
  27.     local indent = string.rep( "  ", lvl )
  28.     local leader = indent .. `({typeof(label)}) {tostring(label)}: `
  29.     if lvl == 0 then  local n,l = debug.info(2, "nl"); leader =  `{ll.GetTimestamp()}: {l} {n}:\n` .. leader end--if (with timestamp)
  30.     typ = typeof( val )
  31.     if typ == "table" then
  32.         rawDBG( `{leader}({typ}) {tostring(val)} `.."{" )
  33.         lvl += 1
  34.         for k,v in pairs(val) do DBG_dump( k, v, lvl ) end--for
  35.         rawDBG( indent .. "}" )
  36.     else rawDBG( `{leader}({typ}) {tostring(val)}` )
  37.     end--if
  38. end--DBG_dump
  39.    
  40. function DBG_dump_list( label, list )  local n,l = debug.info(2, "nl"); rawDBG( `{ll.GetTimestamp()}: {l} {n}: {label}:  {concat(unpack(list))}` ) end--dump a list (1D array) on one line.
  41.  
  42. function DBG_err( ... )
  43.     DBG( "Error Caught by xpcall", ... )
  44.     rawDBG( debug.traceback( "Stack Trace", 2) )
  45. end--DBG_err
  46.  
  47. --[=[]]  --Switch to do-nothing dummy versions when above is disabled.
  48. local function rawDBG() end--rawDBG disabled
  49. local DBG, DBG_dump, DBG_dump_list, = rawDBG, rawDBG, rawDBG
  50. --]=]
  51.  
  52. -->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Code start <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  53.  
  54. --Script Scope Variables
  55. local base = { pos = {}, rot = {} } -- base position and rotation of each link. Stored in LSD, see Setup block belo.
  56. local current = { pos = {}, rot = {} } -- current position  and rotation of each link
  57. local X, Y, Z = {}, {}, {} -- Sets of links to rotate with each Euler axis rotation.
  58. local Xtails, Ytails, Ztails, Alltails = {}, {}, {}, {} --Sets of links to leave particle trails
  59. local center, Xring, Yring, Zring --Ring link numbers
  60. local Xmaj, Ymaj, Zmaj, Xmin, Ymin, Zmin --X, Y, and Z ring major and minor radii.
  61. local STlast -- last valid ST value for use if a touch moves off face.  Assume a touch must always start on face to set.
  62. local center, autoTouch --center of rotation == center of Object, link to touch for auto on.off and reset.
  63. local auto, autoControl, autoTimer
  64. local ComChan = 999 -- Command Channel.  Used both for user commands and inter-object sync/communication.
  65.  
  66. --Utility Functions
  67. local function GetLinkPos( link ) return ll.GetLinkPrimitiveParams( link, {PRIM_POS_LOCAL} )[1] end--GetLinkPos
  68. local function SetLinkPos( link, pos) ll.SetLinkPrimitiveParamsFast( link, {PRIM_POS_LOCAL, pos} ) end--SetLinkPos
  69.  
  70. local function GetLinkRot( link ) return ll.GetLinkPrimitiveParams( link, {PRIM_ROT_LOCAL} )[1] end--GetLinkRot
  71. local function SetLinkRot( link, rot) ll.SetLinkPrimitiveParamsFast( link, {PRIM_ROT_LOCAL, rot} ) end--SetLinkRot
  72.  
  73.  
  74. --init/reset group
  75. local init, reset
  76. function init()
  77.     Xring = tonumber( ll.LinksetDataRead("X Ring") ); Yring = tonumber( ll.LinksetDataRead("Y Ring") ); Zring = tonumber( ll.LinksetDataRead("Z Ring") )
  78.     local tx, ty, tz = ll.GetLinkPrimitiveParams( Xring, {PRIM_SIZE} )[1], ll.GetLinkPrimitiveParams( Yring, {PRIM_SIZE} )[1], ll.GetLinkPrimitiveParams( Zring, {PRIM_SIZE} )[1]
  79.     Xmaj, Ymaj, Zmaj = tx.y/2, ty.y/2, tz.y/2 --Radii are the y (or z) dimension/2 (Setup parameters but faster to calculate than to look up in LSD.)
  80.     Xmin, Ymin, Zmin = tx.x, ty.x, tz.x -- minor radii are the X dimension/2
  81.     center, autoTouch = tovector( ll.LinksetDataRead( "center" ) ), tonumber( ll.LinksetDataRead( "Reference object" ) )
  82.     local floater = tonumber( ll.LinksetDataRead( "Text Floater" ) )
  83.     ll.SetLinkPrimitiveParamsFast( floater, {PRIM_TEXT, `{_Name_}: {_Version_}\n \n{_Desc_}`, vector(1,0.5,0.2), 1} )
  84.  
  85.     base = lljson.sldecode( ll.LinksetDataRead( "base" ) )
  86.     X = lljson.sldecode( ll.LinksetDataRead( "X" ) )
  87.     Y = lljson.sldecode( ll.LinksetDataRead( "Y" ) )
  88.     Z = lljson.sldecode( ll.LinksetDataRead( "Z" ) )
  89.     table.move( X, 1, #X, #Y+1, Y ) -- X moves with Y
  90.     table.move( Y, 1, #Y, #Z+1, Z ) -- Y moves with Z
  91.  
  92.     XT = lljson.sldecode( ll.LinksetDataRead( "XT" ) )
  93.     YT = lljson.sldecode( ll.LinksetDataRead( "YT" ) )
  94.     ZT = lljson.sldecode( ll.LinksetDataRead( "ZT" ) )
  95.     AT = lljson.sldecode( ll.LinksetDataRead( "AT" ) )
  96.  
  97.     reset()
  98. end--init
  99.  
  100. function reset() --Reset positions and rotations to base values
  101.     local b
  102.     for link = 2, ll.GetNumberOfPrims() do
  103.         b = base.pos[link]; SetLinkPos( link, b ); current.pos[link] = b
  104.         b = base.rot[link]; SetLinkRot( link, b ); current.rot[link] = b
  105.     end--for
  106.     autoControl = false; if autoTimer then LLTimers.off(autoTimer); autoTimer = nil; end--if
  107. end--reset
  108.  
  109.  
  110. --Relative Euler Rotations
  111. local function rotX(angle) --Rotate X set about X center relative to the current position
  112.     angle /=2
  113.     local v = -ll.Rot2Up(current.rot[Yring]) * math.sin(angle)
  114.     local rot = quaternion( v.x, v.y, v.z, math.cos(angle) )
  115.     for _, link in X do
  116.         local p = center + (current.pos[link]-center) * rot
  117.         local r = current.rot[link] * rot
  118.         SetLinkPos( link, p ); current.pos[link] = p
  119.         SetLinkRot( link, r ); current.rot[link] = r
  120.     end--for
  121. end--rotX
  122.  
  123. local function rotY(angle) --Rotate Y set about Y center relative to the current position
  124.     angle /=2
  125.     local v = -ll.Rot2Up(current.rot[Zring]) * math.sin(angle)
  126.     local rot = quaternion( v.x, v.y, v.z, math.cos(angle) )
  127.     for _, link in Y do
  128.         local p = center + (current.pos[link]-center) * rot
  129.         local r = current.rot[link] * rot
  130.         SetLinkPos( link, p ); current.pos[link] = p
  131.         SetLinkRot( link, r ); current.rot[link] = r
  132.     end--for
  133. end--rotY
  134.  
  135. local function rotZ(angle) --Rotate Z set about Z center relative to the current position
  136.     local rot = ll.Euler2Rot( vector(0,0,angle) )
  137.     for _, link in Z do
  138.         local p = center + (current.pos[link]-center) * rot
  139.         local r = current.rot[link] * rot
  140.         SetLinkPos( link, p ); current.pos[link] = p
  141.         SetLinkRot( link, r ); current.rot[link] = r
  142.     end--for
  143. end--rotZ
  144.  
  145. --Visual Effects
  146. local function linksetTrail( set, onoff, color ) -- create or stop the particle trail for a given link.  For use with the tails.
  147.     if onoff then
  148.         for _, link in set do
  149.             ll.LinkParticleSystem( link, {
  150.                 PSYS_PART_FLAGS , bit32.bor( PSYS_PART_EMISSIVE_MASK, PSYS_PART_INTERP_COLOR_MASK, PSYS_PART_INTERP_SCALE_MASK, PSYS_PART_RIBBON_MASK ), --PSYS_PART_RIBBON_MASK
  151.                 PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_ANGLE_CONE,
  152.                 PSYS_PART_START_COLOR, color,
  153.                 PSYS_PART_END_COLOR, color/8,
  154.                 PSYS_PART_START_SCALE, vector(0.04,0.04,0),
  155.                 PSYS_PART_END_SCALE, ZERO_VECTOR,
  156.                 PSYS_SRC_TEXTURE, uuid("5e0809d7-368c-cfab-a2ba-dd5a3774eac8"), -- uuid("3553f0aa-23c6-58ed-bf2f-2713e7875b73"),
  157.                 PSYS_PART_START_GLOW, 0.0,
  158.                 PSYS_SRC_MAX_AGE, 0,
  159.                 PSYS_PART_MAX_AGE, 1,
  160.                 PSYS_SRC_BURST_RATE, 0.0,
  161.                 PSYS_SRC_BURST_PART_COUNT, 1,
  162.                 PSYS_SRC_BURST_RADIUS, 0.15,
  163.                 PSYS_SRC_ANGLE_BEGIN, 0,
  164.                 PSYS_SRC_ANGLE_END, 0,
  165.                 PSYS_SRC_BURST_SPEED_MIN, 0,
  166.                 PSYS_SRC_BURST_SPEED_MAX, 0,
  167.         })
  168.         end--for
  169.     else
  170.         for _, link in set do
  171. --            DBG(link)
  172.             ll.LinkParticleSystem( link, {})
  173.         end--for
  174.     end--if
  175. end--linksetTrail    
  176.  
  177.  
  178. --touch activated autoplay
  179. auto = coroutine.create( --auto rotate.  variabale auto declared local near the top of the script.
  180.     function( ) -- autoControl: true -> run  false -> pause until resumed
  181.         while true do
  182.             rotX(0.3); rotY(0.2); rotZ(0.1);
  183. --            llRegionSay( 9999, `qua {current.rot[Xring]}` )
  184.             if autoControl then autoTimer = LLTimers:once( 0.1, function() coroutine.resume( auto ) end ) end--if
  185.             coroutine.yield()
  186.         end--while
  187.     end--function
  188. )--end create auto
  189.    
  190.  
  191. --Events, Touch Group
  192. local longTouchThreshold, longTouchTime, longTouchTimer = 1.5, 0, nil
  193.  
  194. function LLEvents.touch_start(detected)
  195.     local link = detected[1]:getLinkNumber()
  196.     if  link == autoTouch then
  197.         longTouchTime = os.clock() + longTouchThreshold
  198.         longTouchTimer = LLTimers:once( longTouchThreshold, reset )
  199.     else
  200.         LongTouchTime = 0 --flags touch on a link other than autotouch.
  201.         autoControl = false
  202.         linksetTrail( AT, false )
  203.     end--if
  204. end--touch_start
  205.  
  206. function LLEvents.touch(detected)
  207.     local link, ST = detected[1]:getLinkNumber(), detected[1]:getTouchST()
  208.     if ST == TOUCH_INVALID_TEXCOORD then ST = STlast else STlast = ST end--if
  209.     local s = if ST.y < 0.5 then 1 else -1 -- side of ring factor. Perhaps replace with theta adjustment.
  210. --    if face == 1 then  DBG( ST, s, s*(ST.y-0.5)  ) end--if
  211.     local r = ST.x -- distance to move in minor radius units.
  212.     r = 4 * s * (
  213.         if r < 0.5 then 0.25-r -- 0.0 -> 0.5 =>  1 -> -1
  214.         else            r-0.75 -- 0.5 -> 1.0 => -1 ->  1
  215.     )
  216. --    DBG( r, Zmaj, Zmin, r*Zmin/Zmaj )
  217.     if     link == Xring then   rotX( r*Xmin/Xmaj/3 ) -- chord approximation of angle, guaranteed to be less than true angle.  Should
  218.     elseif link == Yring then   rotY( r*Ymin/Ymaj/3 ) -- prevent overshoot, but divide by 3 as an observed fudge factor due, perhaps,
  219.     elseif link == Zring then   rotZ( r*Zmin/Zmaj/3 ) -- to an unfortunate interaction with the simulator's smoothing algorithm.
  220.     --else ignore
  221.     end--if
  222. end--touch_start
  223.  
  224. function LLEvents.touch_end(detected)
  225.     if longTouchTimer then LLTimers:off( longTouchTimer ); longTouchTimer = nil; end--if
  226.     if os.clock() < longTouchTime then --short touch on autotouch link, toggle auto mode.
  227.         autoControl = not autoControl
  228.         if autoControl then
  229.             --linksetTrail( {4}, true, vector(1,0,0) );
  230.             linksetTrail( XT, true, vector(1,0,0) ); linksetTrail( YT, true, vector(0,1,0) );linksetTrail( ZT, true, vector(0,0,1) )
  231.             coroutine.resume( auto )
  232.         else
  233.              if autoTimer then LLTimers:off(autoTimer); autoTimer = nil; end--if
  234.             linksetTrail( AT, false )
  235.         end--if
  236.     end--if
  237. end--touch_end
  238.  
  239.  
  240. --Events, Communication Group
  241. local RESET_MESSAGE = 999
  242. local owner = ll.GetOwner()
  243. ll.Listen( ComChan, "", NULL_KEY, "" )
  244. local rotWarnCount, rotWarnTimer = 0, nil
  245.  
  246. local listenCommands = {
  247.     init = init, reset = reset, --Do I want both?  Unless LSD has changed, init shouldn't do more than reset once the script is running.
  248.     qua = function( data )
  249.         local quat = toquaternion(data)
  250.         if not quat then
  251.             rotWarnCount += 1
  252.             if not rotWarnTimer then LLTimers:once( 1, function()
  253.                 print( `Warning: Ignoring invalid argument to qua command: {data}`..
  254.                     `{if rotWarnCount>1 then " (plus "..tostring(rotWarnCount-1).." similar)" else ""}`
  255.                 )
  256.                 rotWarnCount = 0; rowWarnTimer = nil
  257.             end) end--function, timer, if
  258.         return
  259.         end--if
  260.         reset() --DBG Consider whether there is any advantage computing the move from relative postions.
  261.         DBG( `qua {quat} ({ll.Rot2Euler(quat)})` )
  262.         local euler = ll.Rot2Euler( quat )
  263.         rotX(euler.x); rotY(euler.y); rotZ(euler.z)
  264.     end,--qua
  265. }
  266.  
  267. function LLEvents.listen( chan, name, id, msg ) --no need to check channel, only listening for the one.
  268.     DBG(`chan="{chan}"  name="{name}"  id="{id}"  msg="{msg}"`)
  269.     if ll.GetOwnerKey(id) ~= owner then return end--if
  270.     msg = ll.StringTrim( msg:lower(), STRING_TRIM_HEAD )
  271.     local _, _, command, sep, data = msg:find( "^(.-)([,:< ])(.*)$" )
  272.     if not sep then command, data = msg, ""
  273.     elseif sep == "<" then data = sep..data
  274.     end--if
  275.     DBG(`command="{command}"  sep="{sep}"  data="{data}"`)
  276.     command, data = ll.StringTrim( command, STRING_TRIM), ll.StringTrim( data, STRING_TRIM)
  277.     local t = listenCommands[command]; if t then t( data ) end--if
  278. end--listen
  279.  
  280. function LLEvents.link_message( _, num, _, _ ) if num == RESET_MESSAGE then init() end end--if link_message
  281.  
  282.  
  283. --Startup Group (see also link_message above.)
  284. function LLEvents.on_rez() init() end--on_rez
  285. init()
  286.  
Advertisement
Comments
  • User was banned
  • User was banned
  • User was banned
  • User was banned
Add Comment
Please, Sign In to add comment