Codea 1.5 (Beta 13 & 14)

Hello @Simeon. Does the following code reveal a bug in beta 1.5(14), or is it my code that is buggy?

The mesh renders for a few seconds, then disappears for a short while, then reappears briefly, to disappear for good.

Lua:


--
-- Arc
--

supportedOrientations(LANDSCAPE_ANY)
function setup()
    parameter.number("width", 1, 100, 40)
    parameter.number("radius", 50, WIDTH / 2, 200)
    parameter.number("startAngle", -180, 180, -130)
    parameter.number("endAngle", -180, 180, 130)
    stroke(118, 223, 25)
end

function draw()
    background(64)
    strokeWidth(width)
    arc(WIDTH / 2, HEIGHT / 2, radius, startAngle, endAngle)
end

function arc(x, y, radius, a1, a2)
    local m = mesh()
    m:addRect(x, y, radius * 2, radius * 2)
    m.shader = shader("Documents:Arc")
    m.shader.size = (1 - strokeWidth()/radius) * 0.5
    m.shader.color = color(stroke())
    m.shader.a1 = math.rad(a1)
    m.shader.a2 = math.rad(a2)
    m:draw()
end

Vertex shader:


//
// Vertex shader: Arc
//

uniform mat4 modelViewProjection;

attribute vec4 position;
attribute vec2 texCoord;

varying highp vec2 vTexCoord;

void main() {
    vTexCoord = texCoord;
    gl_Position = modelViewProjection * position;
}

Fragment shader:


//
// Fragment shader: Arc
//
precision highp float;

uniform float size;
uniform float a1;
uniform float a2;
uniform vec4 color;

varying vec2 vTexCoord;

void main() {
    vec4 col = vec4(0.0);
    vec2 r = vTexCoord - vec2(0.5);
    float d = length(r);
    if (d > size && d < 0.5) {
        float a = atan(r.y, r.x);
        if (a2 > a1) {
            if (a > a1 && a < a2) {
                col = color;
            }
        } else {
            if (a > a1 || a < a2) {
                col = color;
            }
        }
    }
    gl_FragColor = col;
}

Hello @Simeon. Could the ‘expression key’ pop up:

(1) in the Shader Lab Editor, include &, |, ^ and :?

(2) be dismissed if a (non-pop up) key is pressed instead?

.@mpilgrem will do on the extra keys — Though I’ve included &&, ||, ^^ instead, as the bit-wise versions are not supported in GLSL. I’ve also put in ? and : because they go together.

(2) might not be possible (it works for other extended keys, and for the delete key) but regular keys are ignored due to the way the popover system works.

.@mpilgrem I’ve tested your Arc code (great shader, by the way). It doesn’t seem to disappear for me — though I’ve been modifying the shader system quite a lot for the next release, and fixed some bugs. I will test it more thoroughly.

BUG More than one call of saveProjectTab only saves the last one.

I also have a strange effect with raw gists. If I load code form a raw gist, it contains HTML special entities, e.g < is represented as <. Loading the same URL with curl is fine, or is curl doing some interpretation behind the scenes for me?

Example (loads aciolino’s take on Cider Controls):


function setup()
    url = "https://gist.github.com/raw/4127177/"
    url = url.."1f5a7859e5a315ff692fd7af8278eb66aeda1312/CiderControls%201.5.2"
    http.request(url, s, f)
end
function s(data, status)
    if s == 200 then print(data) else print("Error: "..status) end
end
function f(err) print(err) end
function draw() end

Some kind of demo of the aforementioned bug. Some half-baked code that loads aciolino’s DropBox gist which contains 3 files. Only Main is being saved. Also: HTML special entities effect.

Usage: Save the script below, let’s name it GistLoader. Create a new project, in this case DropBox. Codea cannot create new projects, it can only modify existing ones. Run GistLoader and provide “DropBox” as project name, hit “create”.


-- GistLoader

-- Cider Controls 1.5.2 from aciolino
local defaulturl = "https://gist.github.com/4127177"
-- DropBox from aciolino, 3files
local defaulturl = "https://gist.github.com/4091848"

function removeCR(text)
    return string.gsub(text, "\\r", "")
end

function extractRawLinks(text)
    print("Extracting raw links")
    local links = {}
    for pat in string.gfind(text, [[title="View Raw" href="(.-)"]]) do
        print(pat)
        -- get tab name
        local start, stop, tab = string.find(pat, ".*/(.*)")
        -- strip .lua extension if found at end of name
        if string.find(tab, ".lua$") then
            tab = string.sub(tab, 1, -5)
        end
        table.insert(links, {link=pat, tab=tab})
    end
    if #links == 1 then
        links[1].tab = "Main"
    end
    for i, l in ipairs(links) do
        print("link= "..l.link.."  tab= "..l.tab)
    end
    return links
end

reqResult = 0
reqData = nil

function request(url)
    reqResult = 0
    http.request(url, reqSuccess, reqFailure)
end

function reqSuccess(data, status)
    print("status = " .. status)
    if status == 200 then
        reqData = data
        reqResult = 1
    else
        print("Communication error")
        reqResult = -1
    end
end

function reqFailure(err)
    print("Network error")
    print(err)
    reqResult = -2
end

state = "idle"

function createFromGist()
    if state == "idle" then
        state = "makebasereq"
    else
        print("Not idle")
    end
end

function setup()
    parameter.text("url", defaulturl)
    parameter.text("projectName")
    parameter.action("create", createFromGist)
end

glinks = nil
gcount = 0

function draw()
    if state == "idle" then
        -- tunix
    elseif state == "makebasereq" then
        request(url)
        state = "waitforbaseresponse"
    elseif state == "waitforbaseresponse" then
        if reqResult > 0 then
            glinks = extractRawLinks(reqData)
            gcount = 1
            state = "requestpart"
        elseif reqResult < 0 then
            state = "error"
        else
            -- wait
        end
    elseif state == "requestpart" then
        local link = glinks[gcount].link
        link = string.gsub(link, " ", "%%20")
        print("Requesting " .. link)
        request("https://gist.github.com" .. link)
        state = "waitpart"
    elseif state == "waitpart" then
        if reqResult > 0 then
            local data = removeCR(reqData)
            local tab = glinks[gcount].tab
            saveProjectTab(projectName .. ":" .. tab, data)
            print("Updating " .. projectName .. ":" .. tab)
            gcount = gcount + 1
            if gcount > #glinks then
                print("All done")
                state = "idle"
            else
                state = "requestpart"
            end
        elseif reqResult < 0 then
            state = "error"
        else
            -- wait
        end
    elseif state == "error" then
        print("An HTTP error occurred ... resetting")
        state = "idle"
    else
        print("ERROR: Unknown state ... resetting")
        state = "idle"
    end
end

The editor is doing funny things when a file contains real tab characters for indentation. It seems that the caret thinks that a tab is wider than is actually rendered. This could be a problem in the future if scripts are not originally developed inside Codea.

.@Codeslinger it sounds like we’ll need to do some clean up in saveProjectTab —

I’ll modify it to replace tabs with spaces. Are there any other special characters that might be an issue? Thanks for the examples.

Unsure of the Gist issue, it could be either Apple’s networking library escaping HTML automatically, or simply how the Gist returns (and curl doing the conversion). I could look at adding some utility functions: http.escapeHTMLString( string ) and http.unescapeHTMLString( string ).

Wouldn’t it be better if the editor knew what a tab is? As I see it it’s just an issue between the rendered width and the width that is assumed for cursor movement.

I’ll try to look deeper into the networking problem. If I fetch files from my computer then http.request works fine and doesn’t escape anything.

.@Codeslinger at the moment it’s not possible in the editor — it seems like a simple task, but the current editor works as a mix of UIKit and Core Text, which causes all sorts of idiosyncrasies in text layout. I’m working on a rewrite of the editor that is due out after 1.5, that should be much more capable. For the moment I will have it replace tabs to ensure the files are editable.

Thank you for looking into the networking issue, if it’s a matter of escaping (or unescaping) the results, then it should be a simple fix.

Would it be possible to shift the search key? I keep hitting it by accident and then having to wait while it indexes my code.

.@Andrew_Stacey How would you re-arrange the keys? I’m open to suggestions on this.

The problem that I have is that my typing is not 100% accurate and sometimes I hit the wrong key. If that’s the search button then it takes a while before I get control back again. So I’d put it somewhere which minimises the chance of accidentally pressing it. Above the Q would be a reasonable place. If completely reorganising that row then I’d group the ones that add characters separately from the meta keys (like search and documentation).

I could swap search with the parentheses () button.

I’d like to keep Tab as the leftmost button, but I don’t mind switching parentheses and search if you feel that would help.

Like this:

That looks much better.

Hello @Simeon, @John. It looks like Simeon’s belief about the extension/repair of the indexing of the physics.body userdata to set the value of general keys in beta 1.5(14) is correct:


--
-- Body
-- in Codea beta 1.5(14)
--

function setup()
    myBody = physics.body(CIRCLE, 10)
    print(type(myBody)) -- Output: userdata
    myBody.myCustomField = "Test"
    print(myBody.myCustomField) -- Output: Test
end

function draw() background(0) end

(Update) However, that is not documented in the in-app reference. You might add something like “In addition, the values of other, user-defined, fields of a body can be set and returned using the same indexing syntax as is used for tables.”

GitHub is a diva!

Fetching a raw gist (see some posts above) with curl on the PC delivers exactly what’s expected , I also added the --raw parameter to make this sure. (I also tried tcpdump until i realized that the connection is HTTP S.)

I tried getting it with luasocket on the PC and get a 301 (moved permanently).

I tried a raw file from a regular repository on GitHub from within Codea and it is not mangled. Only gists. Only from within Codea.

I still don’t understand …

Let’s have some breakfast, I’ll post my problem to the regular forum then to reach a larger audience and the problem itself doesn’t depend on a 1.5 specific feature.

The following code illustrates the difference between how a noSmooth line behaves and how I would have expected it to behave:


--
-- Line
--

function setup()
    parameter.integer("width", 1, 100, 10)
    noSmooth()
end

function draw()
    background(0)
    strokeWidth(width)
    stroke(255, 0, 0)
    line(100, 100, WIDTH - 100, HEIGHT - 300)
    stroke(0, 255, 0)
    myLine(100, 300, WIDTH - 100, HEIGHT - 100)
    fill(0)
    text("Codea's line() (noSmooth())", WIDTH / 2, HEIGHT / 2 - 100)
    text("myLine()", WIDTH / 2, HEIGHT / 2 + 100)
    fill(255)
    text("Codea's line() (noSmooth())", WIDTH / 2 + 1, HEIGHT / 2 - 99)
    text("myLine()", WIDTH / 2 + 1, HEIGHT / 2 + 101)
end

function myLine(x1, y1, x2, y2)
    local dx = x2 - x1
    local dy = y2 - y1
    local len = math.sqrt(dx * dx + dy * dy)
    local mx = x1 + dx / 2
    local my = y1 + dy / 2
    local w = strokeWidth()
    local a = math.atan2(dy, dx)
    local m = mesh()
    m:addRect(mx, my, len, w, a)
    m:setColors(stroke())
    m:draw()
end

.@mpilgrem I’ve changed Codea to draw noSmooth() lines without the GL_LINE primitive.

What was happening before was that Codea was only submitting two vertices to the GPU and telling it to render with GL_LINE. This was extremely cheap, but limited the max stroke width and resulted in cut-off ends (due to the simplistic way the GPU draws lines).

I’ve modified it so that Codea’s lines will be identical to your example (i.e. it uses a filled rect, basically).