MISSION CRITICAL: problem with newLine function

Weird.  As a workaround you could store the previous point and if the current point’s position is too similar simply skip appending it.

Yeah. Before we even selected CSDK for this project. I made a test for streaming data to update graphics.  CSDK was lightning fast compared to our Javascript solution when updating graphics at a rate of 100 times per second.  I didn’t expect any problems with the graphics.  Obviously our test data didn’t have any duplicates.  

Even after trapping the duplicates with a simple boolean == we still saw the wonky behavior.  I woke up this morning with the thought that maybe there is a threshold below which Graphics 2.0 treats the value as the same, even when equivalence tests say it is different.  Seems like that’s what happens.  In normal drawing situations no one would ever append two identical values or put a place holder value at the start that is identical to the original point, so I can see how it might have been missed in development.

I think I’m going to remove duplicates further back in the stream in the c-code that monitors the robot movement.  We update 2-4 iPod touch devices for every data point, so we don’t want to send a value over the network if it isn’t going to be plotted.  We might scale our sample rate for the speed of the robot movement, but I think we’ll just round the values to .0x with c-code, trap the duplicates and send only values that are different enough to be plotted across the network.

I’m actually loving CSDK for our collaborative classroom network projects that use IOS/Android devices.  We do a lot of science and math activities that use robots, probes, sensors, etc., so graphing will be really important for us.  The networking is fast, straight forward and really stable.

Corona Labs has a nice product.

xposition, yposition, xposition, yposition

begining and end of line in the same point - obviouslywrong

What do you mean by “it doesn’t work as expected?”, exactly?

local myLine = display.newLine( group, x1, y1, x2, y2) – Create the initial line

Appending should be fine after that, if small and white (default values being 1px and white for newLine)

Sorry to disappoint you.  The first coordinates are required by newLine, but there is no data to graph.  So the two points are intentionally the same.  When data streams from the server it is appended to the line forming a graph.  If there is a better way to declare a newLine object to fill with a line in real time, we’d be happy to know about it.

We have Cartesian graphing area on which we plot the data arriving from the server.  We simply want to plot the data on the graph.  Each unit is 25 pixels apart.  We are able to plot the graph with hardcoded numbers and with math on numbers.  When we try to use the tonumber() function we get a crazy result that makes little sense.  We are plotting at a rate of about 10 times per second.  Is the speed of the plots part of the problem?

Well, it certainly would help speed if you localize tonumber() first but the speed shouldn’t be a problem. I did a quick test project every 100ms, repeated 300 times, and it worked just fine on my Macbook.

What I would do next is instead of using tonumber() within append, do it seperately. Assign to a variable. then print both the number and format.

local number1 = tonumber(myValue)

print(number1, type(number1)) – ensure it’s definitely numbers you’re working with! Maybe it’s something where string.gsub or whatever you’re using is trying to tonumber() something it can’t actually swallow.

The other question (since I don’t see anything else from your code) is does the line start out fine but go bad? What happens if you force the line to have a minimum distance first? (like, -5, 0, 0, 0)

(It’s hard to debug with so little code to work from…)

you can ignore the fact that your initial 1-point “line” is degenerate - Corona doesn’t care.

and let’s take you at your word that you’re correctly parsing the text to numbers.

here’s the clue’s that i’m picking up on:

  • your coordinates seem to be 0-based, so translation is implied (to keep them all on screen)

  • you want a graph where your units are 25 pixels wide, so scaling is also implied

  • you’ve inserted your line into “graphGroup”

  • you’ve described the result as “crazy” :wink:

is the translation/scaling occuring on the group?

any chance this is old “graphics v1” era code, and you’ve since updated Corona?

have you tried simply adding graphicsCompatibility=1 to config.lua?

Going to be as clear as I can.  I have about 5 hours to get this working or our research opportunity will be severely hampered.

Here is the Line display object:

local primaryGraph = display.newLine(Lib.graph.group, Data.grid.originX, Data.grid.originY, Data.grid.originX, Data.grid.originY) primaryGraph.stroke = gui.magenta primaryGraph.strokeWidth = 20 Data.priGraph = primaryGraph

Here is the code that attempts to update the line:

print("DATA DATA:\>\> ", data) local dtable = string.split(data, "%S+") table.dump(dtable) print("dtable[2] as number", tonumber(dtable[2])) print("dtable[3] as number", tonumber(dtable[3])) local value = tonumber(dtable[3]) \* 25 Data.priGraph:append(Data.grid.originX, Data.grid.originY - value) Lib.robot.position(Data.primaryBot, Lib.affected.graph, Lib.affected.xAxis, Lib.affected.yAxis, tonumber(dtable[2]), tonumber(dtable[3]))

The last line shows that the tonumber() function is working just fine for my other purposes (in this case it is used to check if the number is positive or negative while positioning the robot on the screen. Like tonumber(dtable[2] > 0.

Ouput:

DATA DATA:>> 1 6.98899999999958 1.96590917968751

table.dump…

[1]=1,

[2]=6.98899999999958,

[3]=1.96590917968751,

dtable[2] as number 6.9889999999996

dtable[3] as number 1.9659091796875

Experimenting:

If I declare value outside the scope of the function I’m using and do the following I get the line I expect–increasing along the y axis.

local primaryGraph = display.newLine(Lib.graph.group, Data.grid.originX, Data.grid.originY, Data.grid.originX + 1, Data.grid.originY + 1) value = value + 1 Data.priGraph:append(Data.grid.originX, Data.grid.originY - value)

Notice that I had to add 1 to the second data point in the line to get this effect.

These all work:

value = value + tonumber(“1”)

local dx = tonumber(“0.9854533691406”)

value = value + dx


local dx = tonumber(“0.9854533691406”) * 25

value = value + dx


value = value + tonumber(“0.9854533691406”) * 25

These do not work:

value = tonumber(dtable[3])


local dx = tonumber(dtable[3])

value = value + dx


value = tonumber(“0.9854533691406”) * 100  – Gives a weird triangular shape not a line.


value = tonumber(dtable[3]) * 100    – Gives nothing


local dx = tonumber(dtable[3]) * 100  – Nothing

value = dx


local dx = tonumber(dtable[3]) * 100              – Nothing but local dx = tonumber(“1”) * 100 works

value = value + dx


The code you’ve provided is not very helpful, I’m afraid. You’ve used a couple of non-corona methods (string.split, table.dump, robot.position…), didn’t show us the Runtime structure, didn’t show us how the data string is assembled…it’s just impossible for one of us to test or reconstruct the code you are using. As such, with no obvious typo/format issues there’s no way for us to tell where the problem would be.

(The only other thing I can say is that using big decimal numbers for pixel display is not winning you anything, either. You should be using math.round/ceil/floor to set pixel position unless you have a very good reason not to. But that shouldn’t be breaking your code.)

As best I can tell, this is what you’re doing, and this works fine on sim. If you’re getting strange shapes then it sounds like either string.split or whatever is making your data string is passing off bad / wrong / strange positions. Maybe it’s using group position instead of global position or vice versa. That sort of thing. Hopefully you are not using .anchorChildren either.

 local originX, originY = 100, display.contentHeight - 5 local graph = display.newGroup() -- Create the line object local primary = display.newLine(graph, originX, originY, originX+1, originY) primary.stroke = {1,0,1} -- magenta primary.strokeWidth = 25 local function handleStream() originX = originX + 1.4587588787276 originY = originY - 1.6779827711721 primary:append( originX, originY ) end timer.performWithDelay(100, handleStream, 300)

why don’t you do “something” like below, just to “prove” to yourself if (or not) your string parsing is working, to narrow down the problem, fe:

-- you should use ACTUAL captured data here -- i just made some up local sampleData = { "1 0.6358531449324 -3.0669576097903", "2 0.8500930814539 -0.2012695699942", "1 3.9596240119633 3.2284005249184", "2 -3.2589190343944 3.5894344920194", "2 0.13534958952605 -1.9600512710959", "1 -4.0859706411939 -1.3554795983764", "1 -3.3410138248848 4.8852504043703", "1 -3.8091677602466 -4.9533066805017", "1 -1.221198156682 0.31662953581347", "2 1.0176396984771 1.0716574602496", "1 1.6304513687551 -0.49211096530045", "1 -4.4296090578936 1.0768456068606", "2 3.0260628070925 0.19882808923612", "1 3.7597277748955 2.2667622913297", "2 4.2571794793542 0.39353617969298", "1 -0.37919248023927 -2.6467177343059", "2 -2.9039887691885 2.796563615833", "2 4.9679555650502 4.9969481490524", "2 -1.0756248664815 -2.3378704184088", "1 3.4014404736473 -4.7625659962767", } for i=1,#sampleData do -- i don't have your .split() fn so instead: local botn,botx,boty = string.match(sampleData[i], "(%S\*)%s(%S\*)%s(%S\*)") -- verify got all parts as strings print(i, botn,botx,boty, type(botn),type(botx),type(boty)) -- convert to numbers botn,botx,boty = tonumber(botn),tonumber(botx),tonumber(boty) -- verify got all parts as numbers print(i, botn,botx,boty, type(botn),type(botx),type(boty)) -- double-check by converting back to string, back to number -- NB: this isn't guaranteed to work for EVERY double, -- as error can creep into the conversion, -- but likely ok for your values (and the sample values above): local chkn,chkx,chky = tonumber(tostring(botn)),tonumber(tostring(botx)),tonumber(tostring(boty)) -- then compare: print(i, tostring(botn==chkn), tostring(botx==chkx), tostring(boty==chky)) end

then you can proceed from there, ie: either your parsing is ‘bad’ so focus there, or it’s ‘good’ so focus on the line (where i’d still try v1 compatibilitly as a first step)

fwiw, hth

mystring.split = function (str, pattern, out) out = out or { } for word in string.gmatch(str, pattern) do table.insert(out, word) end return out end

changed the name to make sure I’m not colliding with anything in the string object.

mytable.dump = function (table) local out = "mytable.dump..." for key, value in pairs(table) do out = out.."\n\t["..key.."]="..value.."," end print(out) return out end

This the position function that puts the robot in the correct place.  It also scrolls the screen.  The position function works perfectly using the same data that I’m trying to use for the graph.  It is more complicated because it scrolls the graph behind the robot when it reaches the edge of the screen.

This function has no problems:

 position = function(bot, graph, xAxis, yAxis, xpos, ypos) local function yScroller (yAxis, graph, origin, ypos) if ypos \< 8 and ypos \> -9 then y = ypos elseif ypos \> 7.9999 and ypos \< 18 then y = 8 elseif ypos \< -8.9999 and ypos \> -18 then y = -9 elseif ypos \> 17.9999 and ypos \<= 20 then y = ypos - 10 elseif ypos \< -17.9999 and ypos \>= -20 then y = ypos + 9 elseif ypos \>= 20 then y = 10 elseif ypos \<= -20 then y = -10 end if ypos \< 8 and ypos \> -9 then yAxis.y = yAxis.origin.y graph.y = 0 else yAxis.y = 25 \* (ypos - y) + yAxis.origin.y graph.y = 25 \* (ypos - y) end return y \* -25 + origin end bot.group.y = yScroller (yAxis, graph, bot.group.originY, ypos) graph.x = -25 \* xpos xAxis.x = -25 \* xpos + yAxis.origin.x end

Hi davebolliger,

Forgot to mention that setting v1 compatibility screwed everything up.  I picked up Corona SDK in January 2014.  Don’t think I have old versions anywhere.

I have a kluge working.  It does put a graph down that will work for the students, but it isn’t the clean looking graph I expected.  Need to know how to erase the points and redraw the graph for the next run.  Don’t see a method for this in the Line object.  Looks like I’ll have to kill the first one and drop another, unless there is a better way.

Still interested in a clean solution to this problem.  I did use data like the above for a test, took it straight from the data server output.  The results are the same.  Odd the the position function moves the robot perfectly but, the line function wonks out.

Here is the kluge:

Line object:

primaryGraph = display.newLine(Lib.graph.group, Data.grid.originX, Data.grid.originY, Data.grid.originX + 1, Data.grid.originY + 1) primaryGraph.stroke = botColor[Data.color.index] primaryGraph.strokeWidth = 10

Function that updates it:

local valueX = 0 local valueY = 0 local stable = {} onDataReceive = function (data) print("DATA DATA:\>\> ", data) stable = mystring.split(data, "%S+") -- mytable.dump(dtable) if stable[1] == "1" then print ("tonumber(stable[3])",tonumber(stable[3]), type(tonumber(stable[3]))) stable[3] = tonumber(stable[3]) local dy = stable[3] \* 25 local dx = tonumber(stable[2]) \* 25 print(type(dx)) valueY = valueY \* .01 + dy valueX = dx Data.priGraph:append(Data.grid.originX + valueX, Data.grid.originY - valueY) Lib.robot.position(Data.primaryBot, Lib.affected.graph, Lib.affected.xAxis, Lib.affected.yAxis, tonumber(stable[2]), tonumber(stable[3])) else end end

Hi Everyone,

Unfortunately we missed our chance yesterday.  However, we have 5 more days of data collection scheduled for the next two weeks.  I set up a test program that isolated the problem.  

Basically, the newLine object is extremely finicky about consecutive duplicate values or values that are very close.  These occur quite often if the robot is moving very slowly.  Identical values interrupt the line and values that are very close produce erratic results  I have attached a copy for others to review.

Thanks for all your efforts.  Perhaps Corona Labs might want to take a look at this.

local storyboard = require "storyboard" local scene = storyboard.newScene() local centerX = display.contentCenterX local centerY = display.contentCenterY local unitSize = 100 -- Data with duplicate values local data = { "1 -3.98 0.01 ", "1 -3.75 0.12 ", "1 -3.49 0.29 ", "1 -3.34 0.41 ", "1 -3.03 0.68 ", "1 -3.03 0.68 ", "1 -3.03 0.68 ", "1 -2.88 0.81 ", "1 -2.88 0.81 ", "1 -2.88 0.81 ", "1 -2.74 0.95 ", "1 -2.60 1.06 ", "1 -2.60 1.06 ", "1 -2.60 1.06 ", "1 -2.60 1.06 ", "1 -2.44 1.18 ", "1 -2.28 1.32 ", "1 -2.28 1.32 ", "1 -2.28 1.32 ", "1 -2.28 1.32 ", "1 -2.13 1.45 ", "1 -1.99 1.58 ", "1 -1.83 1.71 ", "1 -1.69 1.83 ", "1 -1.53 1.95 " } --[[-- Data cleaned to remove duplicate values local data = { "1 -3.98 0.01 ", "1 -3.75 0.12 ", "1 -3.49 0.29 ", "1 -3.34 0.41 ", "1 -3.03 0.68 ", "1 -2.88 0.81 ", "1 -2.74 0.95 ", "1 -2.60 1.06 ", "1 -2.44 1.18 ", "1 -2.28 1.32 ", "1 -2.13 1.45 ", "1 -1.99 1.58 ", "1 -1.83 1.71 ", "1 -1.69 1.83 ", "1 -1.53 1.95 " }]] local function extract (str, pattern) local out = {} for word in string.gmatch(str, pattern) do if type(tonumber(word)) ~= "number" then print("not a number") else table.insert(out, tonumber(word)) end end return out end function scene:createScene (event) local group = self.view local startLocs = extract(data[1], "%S+") local startX = centerX + startLocs[2] \* unitSize local startY = centerY + startLocs[3] \* unitSize local testGraph = display.newLine(group, startX, startY, startX + 1, startY + 1) testGraph.stroke = {1,.2,1} testGraph.strokeWidth = 10 local datum = 1 local addSegment = {} function addSegment:timer (event) print ("string of data: ", data[datum]) local dtable = extract(data[datum], "%S+") print ("dtable[2] number?: "..type(dtable[2]), "value: "..dtable[2]) print ("dtable[3] number?: "..type(dtable[3]), "value: "..dtable[3]) testGraph:append(centerX + dtable[2] \* unitSize, centerY - dtable[3] \* -unitSize) if datum == #data then timer.cancel(event.source) end datum = datum + 1 end timer.performWithDelay(100, addSegment, 0) end scene:addEventListener("createScene", scene) return scene