#### 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 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.

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
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)

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:

• 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?

• Posts: 65

@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 • Posts: 65

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()

if exe then
exe()
else
str = buffer:toStringWithoutActiveLine()
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()
if exe then
exe()
else
str = buffer:toStringWithoutActiveLine()
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

``````