Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Basic MathStudio type app

edited March 2012 in Code Sharing Posts: 65

Tried to write a code similar to app called "MathStudio" but with a very basic functionality. I have used Simeon's "Buffer" class here. Thanks Simeon.

Demo:

Main:

-- Use this function to perform your initial setup
function setup()
    displayMode(STANDARD)
    displayMode(FULLSCREEN)
    screenWidth = 500
    screenHeight = 400
    drawFlag = false
    initFlag = false
    buffer = Buffer()
    xcounter = 0
    result = ""
    errorMsg = ""

    graphTable = {}

    -- Initializing Textfields
    textField = TextField("Formula", 500, 80)
    resultField = TextField("Result", 500,40)
    -- Initializing Buttons
    button = {}
    titles = {"sin()", "cos()", "tan()", "asin()", "acos()", "atan()", "log10()", "log()", "exp()","pow()", "sqrt()", "pi", "+", "-", "*", "/", "%", "(", ")", ",", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0",".", "x", "Solve", "Plot", "Clear","Del", "<==", "==>",}

    counter = 1
    for _, v in pairs (titles) do

        if counter < 13 then
            fill(61, 71, 94, 156)
            r,g,b,a = fill()

        elseif counter >= 13 and counter <= 20 then
            fill(61, 113, 31, 155)
            r,g,b,a = fill()

        else 
            fill(0, 0, 0, 160)
            r,g,b,a = fill()

        end
        table.insert(button,Buttons(v,r,g,b,a,255,255,255,255,15,24))
        counter = counter + 1
    end
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(173, 173, 173, 93)

    initializeScreen()

    drawButtons()
    textField.pos = vec2(centerPnt.x,HEIGHT - HEIGHT*0.1)
    textField:draw() 

    resultField.pos = vec2(centerPnt.x,HEIGHT - HEIGHT*0.18)
    resultField:draw() 

    equationStr = buffer:toString()

    if initFlag then  
        generateEquation()
        if equationStr ~= "y=" then
                initializeTable()
                executeEquation()
                result = y
        else
            errorMsg = "Not a valid equation"
        end
    end
    fontSize(16)
    l,t = textSize(result)
    text(result, resultField.pos.x-resultField.size.x/2+l/2+5,resultField.pos.y )
    text(errorMsg,WIDTH*0.2,HEIGHT-HEIGHT*0.1)

    if drawFlag==true and equationStr ~= "y=" then
       drawCurves()
       spriteMode(CENTER)
       sprite(graph,centerPnt.x,centerPnt.y)
    else
        drawFlag=false
    end 
    buffer:draw()
end

function drawButtons()
    left = WIDTH - WIDTH*0.92
    top = HEIGHT - HEIGHT * 0.265
    counter = 1

    for _, b in pairs(button) do
        b.pos = vec2(left,top)
        b:draw()

        left=left+b.size.x+5
        if counter == 4 then
            counter = 1
            left = WIDTH - WIDTH * 0.92
            top = top - b.size.y-5
        else
            counter = counter + 1
        end
    end
end

function touched (touch)

    for _, b in pairs (button) do
        b:touched (touch)
    end    
end

Buffer:

Buffer = class()

function Buffer:init()
    self.buffer = {}

    self.font = "Inconsolata"
    self.fontSize = 20

    -- x = line, y = pos
    self.cursor = vec2(1,1)

    self.t = 0
    self.cursorBlink = 0
end

function Buffer:setStyle()
    textMode(CORNER)
    font( self.font )
    fill(0, 0, 0, 255)
    fontSize( self.fontSize )
    textWrapWidth(500)
end

function Buffer:cursorToScreen(c)
    -- Get a subset of the buffer
    local lines = table_slice( self.buffer, 1, c.x )
    local l = self.buffer[c.x]

    local upToStr = self:bufferToString(lines)
    local lstr = nil

    if l then
        lstr = table.concat(l)
    end

    pushStyle()

    self:setStyle()

    local lw = 0

    local _,lh = textSize("A")
    local emptyLine = true

    if lstr and lstr ~= "" then
        lw,lh = textSize(string.sub(lstr,1,c.y - 1))
        emptyLine = false
    end

    local pw,ph = textSize(upToStr)

    if emptyLine then
        ph = ph + lh
    end

    popStyle()

    return lw,(ph - lh/2)
end

function Buffer:bufferToString(b)
    local bstrings = {}
    for k,v in pairs(b) do
        table.insert(bstrings, table.concat( b[k] ) )
    end

    return table.concat( bstrings, "\n" )
end

function Buffer:moveCursor(o)
    self.cursor = self.cursor + o

    self.cursor.x = math.max(1, math.min(self.cursor.x, #self.buffer))

    local l = self.buffer[self.cursor.x]
    local y = self.cursor.y

    y = math.max(1, math.min(y, #l + 1))
    self.cursor.y = y
end

function Buffer:insertCharacter(c)
    if c == "x" then
        xcounter = xcounter + 1
    end
    local l = self.buffer[self.cursor.x]

    if l == nil then
        l = {}
        self.buffer[self.cursor.x] = l
    end

    if c == "\n" then
        local start = table_slice(l,1,self.cursor.y)
        local tail = table_slice(l,self.cursor.y+1,#l)


        table[self.cursor.x] = start
        table.insert(self.buffer, self.cursor.x+1, tail)

        self.cursor = vec2( self.cursor.x + 1, 1 )
    elseif c == "Del" then
        if self.cursor.y == 1 then
            -- delete line
            local prevLine = self.buffer[self.cursor.x - 1]

            table.remove(self.buffer, self.cursor.x)

            if prevLine then
                self.cursor = vec2(self.cursor.x - 1, #prevLine + 1)
                table_append( prevLine, l )
            end
        else
            -- delete character

            if (l[self.cursor.y-1] == "x") then
                xcounter = xcounter-1
            end

            table.remove(l, self.cursor.y - 1 )

            self.cursor = vec2(self.cursor.x, self.cursor.y - 1 )
        end
    else
        table.insert(l, self.cursor.y, c)

        self.cursor = vec2( self.cursor.x, self.cursor.y + 1 )

    end

end

function Buffer:draw()
    self.t = self.t + 8 * DeltaTime
    self.cursorBlink = (math.sin(self.t) + 1) * 128

    pushStyle()

    self:setStyle()

    local str = self:toString()
    local w,h = textSize(str)

    pushMatrix()

    translate(textField.pos.x-textField.size.x/2+5,-40)
    text(str, 0, HEIGHT - h)


    -- Draw cursor
    -- Cursor pos x,y
    local cpx,cpy = self:cursorToScreen(self.cursor)

    fill(0, 87, 255, self.cursorBlink)
    rectMode(CENTER)
    rect(cpx,HEIGHT - cpy,5,30)

    popMatrix()
    popStyle()
end

function Buffer:clear()
    self.cursor = vec2(1,1)
    self.buffer = {}
end

function Buffer:toString()
    return self:bufferToString(self.buffer)
end

function Buffer:toStringWithoutActiveLine()
    local bstrings = {}
    for k,v in pairs(self.buffer) do
        if k ~= self.cursor.x then
            table.insert(bstrings, table.concat( self.buffer[k] ) )
        end
    end

    return table.concat( bstrings, "\n" )
end

Other classes and function file is given in the next post as its not allowing to enter more characters in this post.

Tagged:

Comments

  • Posts: 49

    I try themcode an i have find this: 999999999999 
    And the code not works But i delet this and put 0

  • Posts: 49

    Ups i find this


    <a href="tel:999999999999" x-apple-data-detectors="true" x-apple-data-detectors-result="1">999999999999</a> 
  • Posts: 2,161

    Very nice. I had a minor suggestion from watching the video. When pressing sin() then the cursor should be placed in the braces, á la Codea.

    What ideas do you have for extending it?

  • @Andrew: I had that idea in mind and probably I will try to do that but to be honest I already had some problems dealing with the cursor. For example when you type a long equation and I untried to warp the text within the text field so it breaks down to secon line but the cursor remain at the right. I spent few times to deal with it but unfortunately failed to fix it. Probably Simeon can give me an idea how to fix it and that will also help me to extend the idea you gave me. I m still novice :)

  • Codes Continuing

    TextField

    TextField = class()
    
    function TextField:init(title, w, h)
        -- you can accept and set parameters here
        self.title = title
        self.pos = vec2(0,0)
        self.size = vec2(w,h)
        self.color = color(255, 255, 255, 255)
    
    end
    
    
    function TextField:draw()
        -- Codea does not automatically call this method
    
        fill(self.color)
        strokeWidth(3)
        stroke(190, 190, 190, 255)
        rectMode(CENTER)
        rect(self.pos.x,self.pos.y,self.size.x,self.size.y)
        strokeWidth(2)
        stroke(161, 162, 163, 255)
        rect(self.pos.x,self.pos.y,self.size.x-4,self.size.y-4)
        fill(0, 0, 0, 255)
        textMode(CENTER)
        --text(formulaStr, self.pos.x,self.pos.y)
    
    end
    
    function TextField:hit(p)
        local l = self.pos.x - self.size.x/2
        local r = self.pos.x + self.size.x/2
        local t = self.pos.y + self.size.y/2
        local b = self.pos.y - self.size.y/2
        if p.x > l and p.x < r and
           p.y > b and p.y < t then
            return true
        end
    
        return false
    end
    
    function TextField:touched(touch)
        -- Codea does not automatically call this method
        if touch.state == ENDED and self:hit(vec2(touch.x,touch.y)) then
    
        end
    end
    
    

    Buttons:

    Buttons = class()
    
    function Buttons:init(title,r,g,b,a,fr,fg,fb,fa,fnt,h)
        -- you can accept and set parameters here
        self.title = title
        self.pos = vec2(0,0)
        self.size = vec2(0,0)
        self.fntSize = fnt
        self.height = h
        self.action = function() self:buttonAction(self.title) end
    
        self.fr = fr
        self.fg = fg
        self.fb = fb
        self.fa = fa
    
        self.color = color(r,g,b,a)
    
    end
    
    function Buttons:hit(p)
        local l = self.pos.x - self.size.x/2
        local r = self.pos.x + self.size.x/2
        local t = self.pos.y + self.size.y/2
        local b = self.pos.y - self.size.y/2
        if p.x > l and p.x < r and p.y > b and p.y < t then
            return true
        else
            return false
        end
    end
    
    function Buttons:draw()
        -- Codea does not automatically call this method
        pushStyle()
        fill(self.color)
        font("ArialRoundedMTBold")
        fontSize(self.fntSize)
        -- use longest sound name for size
    
        local w,h = textSize("log10") 
        w = w + 15
        h = h + self.height
        noStroke()
        rectMode(CENTER)
        rect(self.pos.x,self.pos.y,w,h)
    
        self.size = vec2(w,h)        
        textMode(CENTER)
        fill(self.fr,self.fg,self.fb,self.fa)
        text(self.title,self.pos.x,self.pos.y)
    
        popStyle()
    end
    
    function Buttons:buttonAction(title)
    
        if title == "Clear" then
            resetTodefault()
        elseif title == "Plot" then
            initFlag = true
            drawFlag = true  
        elseif title == "Del" then
            buffer:insertCharacter(title)
        elseif title == "Solve" then
            if xcounter == 0 then
                initFlag = true
                drawFlag = false 
            end
        elseif title == "<==" then
            buffer:moveCursor(vec2(0,-1))
        elseif title == "==>" then
            buffer:moveCursor(vec2(0,1))
        elseif string.len(title) == 1 then
            buffer:insertCharacter(title)
        elseif string.len(title) > 1 then
    
            for p=1,string.len(title) do
                buffer:insertCharacter(string.sub(title,p,p))
                p = p + 1
            end
    
        end 
    end
    
    
    function Buttons:touched(touch)
    
        -- Codea does not automatically call this method
            if touch.state == ENDED and self:hit(vec2(touch.x,touch.y)) then
                if self.action then
                    self.action()
                end
            end
    end
    
    
  • edited March 2012 Posts: 65

    Codes Continuing:

    Functions:

    Functions
    function generateEquation()
        for n = 1,12 do 
            if titles[n]~="pi" then
                func= string.sub(titles[n],1,4)
            else
                func = titles[n]
            end
                length = string.len(equationStr)
                i =1 
                while i<length do
                    chk=nil
                    local s =  string.find(equationStr,func,i,true)
                    if s~= nil and s>1 then
                       chk = string.sub(equationStr,s-1,s-1)
                    end
    
                    if s ~= nil and chk ~= "a" then
                        sub1 = string.sub(equationStr,1,s-1)
                        sub2 = string.sub(equationStr,s,string.len(equationStr))
                        equationStr = sub1.."math."..sub2
                        length = string.len(equationStr)
                        i = s + 5 + string.len(func)
                        s=nil
                    else 
                        i = i+1
                    end                
                end 
            n = n + 1
        end
    
        equationStr = "y="..equationStr
        initFlag = false
    end
    
    function drawCurves()
        for i = 1,500 do
                ycor = screenHeight/2+graphTable[i]*ratio*0.8         
                lineCapMode(SQUARE)
                strokeWidth(3)
                fill(0, 0, 0, 255)
                fontSize(16)
                stroke(0, 0, 0, 255) 
    
                if ymax == graphTable[i] then
                    txt = "ymax = "..ymax
                    l,t = textSize(txt)
                    text(txt, centerPnt.x-screenWidth/2-l/2-5,centerPnt.y+ycor-screenHeight/2)
                    line(centerPnt.x-screenWidth/2-5,centerPnt.y+ycor-screenHeight/2,centerPnt.x-screenWidth/2+5,centerPnt.y+ycor-screenHeight/2)
                end
    
    
                if ymin == graphTable[i] and ymin~=ymax then
                    txt = "ymin = "..ymin
                    l,t = textSize(txt)
                    text(txt, centerPnt.x-screenWidth/2-l/2-5,centerPnt.y+ycor-screenHeight/2)
                    line(centerPnt.x-screenWidth/2-5,centerPnt.y+ycor-screenHeight/2,centerPnt.x-screenWidth/2+5,centerPnt.y+ycor-screenHeight/2)       
                end
    
                grcolor = color(27, 30, 145, 255)
                if graphTable[i] >= ymin and graphTable[i]<=ymax then
                    graph:set(i,ycor,grcolor)       
                end 
        end
        --fontSize(16)
        l,t = textSize("xmax = -5")
        text("xmax = 5", centerPnt.x+screenWidth/2,centerPnt.y-screenHeight/2-30)
        text("xmin = -5", centerPnt.x-screenWidth/2,centerPnt.y-screenHeight/2-30)
        text("2.5", centerPnt.x+screenWidth/4,centerPnt.y-screenHeight/2-30)
        text("-2.5", centerPnt.x-screenWidth/4,centerPnt.y-screenHeight/2-30)
        text("0", centerPnt.x,centerPnt.y-screenHeight/2-30)
        line(centerPnt.x+screenWidth/4,centerPnt.y-screenHeight/2-5,centerPnt.x+screenWidth/4,centerPnt.y-screenHeight/2+5)
        line(centerPnt.x-screenWidth/4,centerPnt.y-screenHeight/2-5,centerPnt.x-screenWidth/4,centerPnt.y-screenHeight/2+5)
    
    
        txt = "0"
        l,t = textSize(txt)
        text(txt, centerPnt.x-screenWidth/2-l/2-5,centerPnt.y)
    
        --[[txt = ymax/2
        l,t = textSize(txt)
        text(txt, centerPnt.x-screenWidth/2-l/2-5,centerPnt.y)
    
        txt = ymin/2
        l,t = textSize(txt)
        text(txt, centerPnt.x-screenWidth/2-l/2-5,centerPnt.y)--]]
    
    
    end
    
    
    function initializeTable()
    
        graph = nil
        graph = image(screenWidth,screenHeight)
        x=-5
        ymax = 0
        ymin = 999999999999  
    
        for i=1,500 do
            xpos=i
    
           -- executeEquation()
    
            local exe = loadstring(equationStr)  
            if exe then 
                exe()
            else
                str = buffer:toStringWithoutActiveLine()
                exe = loadstring(equationStr)
                if exe then 
                    exe() 
                end
            end 
    
    
            if y > ymax then
                ymax = y
            end
            if y < ymin then
                ymin = y
            end
            graphTable[i] = y
            x=x+0.02
           -- print(graphTable[i])
        end
    
        if ymax > math.abs(ymin) then
            max = ymax
        else
            max = math.abs(ymin)
        end
    
        if max >= 1 then
            ratio = (screenHeight/2)/max
        else
            ratio = screenHeight/2
        end
    
    end
    
    
    
    function initializeScreen()
        noStroke()
        fill(255, 255, 255, 155)  
        rectMode(CENTER)
        centerPnt = vec2(WIDTH*0.72,HEIGHT/2)
        rect(centerPnt.x, centerPnt.y, 500,400)
        strokeWidth(3)
        stroke(132, 131, 131, 255)
        line(centerPnt.x-250,centerPnt.y,centerPnt.x + 250,centerPnt.y)
        line(centerPnt.x,centerPnt.y-200,centerPnt.x,centerPnt.y+200)
        strokeWidth(2)
        for i=1,500 do
            if i%5==0 then
            line(centerPnt.x-250+i,centerPnt.y-200,centerPnt.x-250+i,centerPnt.y+200)
            end
        end
    
        for i=1,400 do
            if i%5==0 then
            line(centerPnt.x-250,centerPnt.y-200+i,centerPnt.x +250,centerPnt.y-200+i)
            end
        end
    end
    
    function executeEquation()
            exe = loadstring(equationStr)  
            if exe then 
                exe()
            else
                str = buffer:toStringWithoutActiveLine()
                exe = loadstring(equationStr)
                if exe then 
                    exe() 
                end
            end 
    end
    function resetTodefault()
        buffer:clear()
        drawFlag = false 
        graph=nil
        xcounter = 0
        result = ""
        errorMsg = ""
    end
    
    function table_append (t1, t2)
        local t1s = #t1
        for k,v in pairs(t2) do t1[k + t1s] = v end
    end
    
    function table_slice (values,i1,i2)
        local res = {}
        local n = #values
        -- default values for range
        i1 = i1 or 1
        i2 = i2 or n
        if i2 < 0 then
            i2 = n + i2 + 1
        elseif i2 > n then
            i2 = n
        end
    
        if i1 < 1 or i1 > n then
            return {}
        end
    
        local k = 1
    
        for i = i1,i2 do
            res[k] = values[i]
            k = k + 1
        end
    
        return res
    end
    
    
Sign In or Register to comment.