Howdy, Stranger!

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

Diagram Drawing

edited April 2012 in General Posts: 76

I want to share a video about my diagram drawing. It can be many shape types (circle, square, diamon or round square) and can connect two shapes with a straight line or spline. Right now, it doesn't provide editor. Here is the sample code

    s = Shape('IT Department',100,200,ROUNDRECT,color(0,255,0,255),color(54, 184, 20, 255))
    c = Shape('Users Acceptance',300,400,CIRCLE,color(255,0,0,255))
    l = Connector(s,4,c,1)
    n= Shape('IT Success',600,600,RECT,color(255,0,255,255))
    l = Connector(c,2,n,3)
    d= Shape('Performance',600,300,DIAMON,color(0,255,255,255))
    l = Connector(n,2,d,3)
    g= Shape('IT Growth',700,300,DIAMON,color(255,255,0,255))
    l = Connector(n,2,g,3)

Edit : To drag a line for creating a connector, you have to click the shape and it will show four dots. Drag a line from the dot to the target shape.



To move the shape, click the shape and drag to move the shape.



By default, the line style is Arrow-End style. You can change to ARROW_NONE, ARROW_START or ARROW_BOTH.

The line type by default is LINE_CURVE which uses the spline.

Comments

  • Posts: 447

    this is genius! It sounds super useful

  • Posts: 2,820

    Are you planning on adding an editor? This is amazing! Id be willing to team up if you don't feel like doing it yourself.

  • Posts: 1,593

    Hi Sanit,

    Very impressive - shades of mind mapping or flow sheeting. The routines could be very useful. Have you thought about having handles on the on jets - I've used LabView which uses objects linked in this fashion to build up applications. Building a language like that would be great to use and a good teaching tool.

    Bri_G

    :)

  • Posts: 76

    @Zoyt I would like to add an editor as Spritely but it will take a lot of time for me. I will post the code here. It still has bugs and lacks a lot of functionality.

    @Bri_G Thanks I used to use the Connector on Squeak created by Ned Konz. I was very surprised how he created the Connector. 

    Thank you for coding from this forum 

    Splines by @Nat 

    RoundRect by @Andrew_Stacey 

    Sorry for no coding comment.

    Enjoin with it!

  • Posts: 76
    --main
    
    -- Use this function to perform your initial setup
    
    function setup()
        displayMode(FULLSCREEN)
        s = Shape('IT Department',100,200,ROUNDRECT,color(0,255,0,255),color(54, 184, 20, 255))
        c = Shape('Users Acceptance',300,400,CIRCLE,color(255,0,0,255))
        l = Connector(s,4,c,1)
        n= Shape('IT Success',600,600,RECT,color(255,0,255,255))
        l = Connector(c,2,n,3)
        d= Shape('Performance',600,300,DIAMON,color(0,255,255,255))
        l = Connector(n,2,d,3)
        g= Shape('IT Growth',700,300,DIAMON,color(255,255,0,255))
        l = Connector(n,2,g,3)
        --[[
        --You can save drawing in local data by double touch on screen
        --then you read it back by calling below code
        drawdata = loadstring(readLocalData('DrawData'))
        a = drawdata()
        --]]
    end
    
    -- This function gets called once every frame
    function draw()
        background(255, 255, 255, 255)
        World():draw()
    end
    
    function touched(touch)
        World():touched(touch)
    end
    
  • Posts: 76
    --World
    
    NEW_LINE = 1
    _substances = {}
    _mode = nil
    _startobj = nil 
    _startpoint = nil
    _touchHandle = false
    
    World = class()
    
    function World:init(x)
        -- you can accept and set parameters here
    
    end
    
    function World:add(s)
        -- you can accept and set parameters here
        table.insert(_substances,s)
    end
    
    function World:draw()
        -- Codea does not automatically call this method
        for i,v in pairs(_substances) do
            v:draw()
        end 
        if _mode == NEW_LINE then
            if CurrentTouch.state==MOVING then
                strokeWidth(5)
                stroke(0)
                line(_startobj:getPoint(_startpoint).x,_startobj:getPoint(_startpoint).y,
                            CurrentTouch.x,CurrentTouch.y)
            elseif CurrentTouch.state==ENDED then
                _mode = nil
                _startobj = nil
                _startpoint= nil
            end 
        end    
    end
    
    function World:touched(touch)
        -- Codea does not automatically call this method
        _touchHandle = false
        for i,v in pairs(_substances) do
            v:touched(touch)
            if _touchHandle then
                break
            end
        end
        if touch.tapCount==2 and touch.state == ENDED then
            self:save()
        end
    end
    
    function World:save()
        -- Codea does not automatically call this method
        clearOutput()
        local cmd = ''
        sp={}
        for i,v in ipairs(_substances) do
            if v.type== LINE then
                cmd = cmd.."s"..i.." = Connector("..sp[v.shape1]..","
                            ..v.point1..","..sp[v.shape2]..","
                            ..v.point2..")\n"
            else
                local c = v.color
                local fc = v.fillColor
                cmd = cmd.."s"..i.." = Shape('"..v.label.."',"..
                      v.x..","..v.y..","..v.type..",color("..c.r..
                      ","..c.g..","..c.b..","..c.a..")"..
                      ",color("..fc.r..","..fc.g..","..fc.b..","..fc.a.."))\n"
                sp[v] = "s"..i
            end
        end
        saveLocalData('DrawData',cmd)
        print("Data are saved completely.")
    end
    
    
  • Posts: 76
    --RoundRect
    function roundRect(x,y,w,h,r)
        pushStyle()
        
        insetPos = vec2(x+r,y+r)
        insetSize = vec2(w-2*r,h-2*r)
        
        --Copy fill into stroke
        stroke(stroke())
        fill(fill())
        noSmooth()
        rectMode(CORNER)
        rect(insetPos.x,insetPos.y,insetSize.x,insetSize.y)
        
        if r > 0 then
            smooth()
            lineCapMode(ROUND)
            strokeWidth(r*2)
    
            line(insetPos.x, insetPos.y, 
                 insetPos.x + insetSize.x, insetPos.y)
            line(insetPos.x, insetPos.y,
                 insetPos.x, insetPos.y + insetSize.y)
            line(insetPos.x, insetPos.y + insetSize.y,
                 insetPos.x + insetSize.x, insetPos.y + insetSize.y)
            line(insetPos.x + insetSize.x, insetPos.y,
                 insetPos.x + insetSize.x, insetPos.y + insetSize.y)            
        end
        popStyle()
    end
    
  • Posts: 76
    --Shape
    
    RECT = 1
    ROUNDRECT = 2
    CIRCLE = 3
    DIAMON = 4
    ELLIPSE = 5
    SQUARE = 6
    NORMAL = 1
    EDIT = 2
    
    Shape = class()
    
    function Shape:init(label,x,y,t,c,fc)
        self.x = x
        self.y = y
        self.label = label
        self.font = "GillSans"
        self.fontSize = 16
        self.textColor = color(0,0,0,255)
        self.width = 65
        self.height = 65
        self.color = c or color(0,255,0,255)
        self.fillColor = fc or color(self.color.r,self.color.g,self.color.b,100)
        self.type = t or RECT
        self.mode = NORMAL
        World():add(self)
    end
    
    function Shape:draw()
        pushStyle()
            --determine width and height of the shape
            font(self.font)
            fontSize (self.fontSize)
            self.width,self.height = textSize(self.label)
            if self.width >= self.height then
                self.width = self.width + 20
                self.height = self.width
            else
                self.height = self.height + 20
                self.width = self.height
            end
            if self.height< 65 or self.width <65 then
                self.width = 65
                self.height = 65
            end
            strokeWidth(5)
            stroke(self.color)
            fill(self.fillColor)
            if self.type == RECT then
                rectMode(CENTER)
                rect(self.x,self.y,self.width,self.height)
            elseif self.type == ROUNDRECT then
                roundRect(self.x-self.width/2,self.y-self.height/2,self.width,self.height,40)
                stroke(self.fillColor)
                roundRect(self.x-self.width/2+3,self.y-self.height/2+3,self.width-6,self.height-6,40)
            elseif self.type == CIRCLE then
                ellipseMode(CENTER)
                ellipse(self.x,self.y,self.width,self.height)
            elseif self.type == ELLIPSE then
                ellipseMode(CENTER)
                ellipse(self.x,self.y,self.width+25,self.height)
            elseif self.type == DIAMON then
                pushMatrix()
                translate(self.x,self.y)
                rectMode(CENTER)
                rotate(45)
                rect(0,0,self.width,self.height)
                popMatrix()
            end
            -- draw text
            textMode(CENTER)
            font(self.font)
            fontSize(self.fontSize)
            fill(self.textColor)
            text(self.label,self.x,self.y)
            if self.mode == EDIT then
                pushStyle()
                    strokeWidth(5)
                    stroke(127, 127, 127, 255)
                    fill(127,127,127, 255)
                    rect(self.x-self.width/2,self.y,10,10)
                    rect(self.x+self.width/2,self.y,10,10)
                    rect(self.x,self.y-self.height/2,10,10)
                    rect(self.x,self.y+self.height/2,10,10)
                popStyle()
            end
        popStyle()
    end
    
    function Shape:getPoint(pt)
        local x,y
        if pt == 1 then
            x,y = self.x-self.width/2,self.y
        elseif pt == 2 then
            x,y = self.x+self.width/2,self.y
        elseif pt == 3 then
            x,y = self.x,self.y-self.height/2
        elseif pt == 4 then
            x,y = self.x,self.y+self.height/2
        end
        return vec2(x,y)
    end
    
    function Shape:touched(touch)
        local v = vec2(touch.x,touch.y)
        if touch.x >= self.x - self.width/2 and touch.x <= self.x + self.width/2 and
            touch.y >= self.y - self.height/2 and touch.y <= self.y + self.height/2 then
            --click for edit mode
            if touch.state == BEGAN then
                if self.mode == NORMAL then
                    self.mode = EDIT
                end 
            end
            --moving 
            if v:dist(vec2(self.x,self.y))<= 20 and self.mode == EDIT 
                and touch.state == MOVING then
                    self.x = touch.x
                    self.y = touch.y
                    _touchHandle = true
            end
            --adjusting size
            if v:dist(vec2(self.x+self.width/2,self.y))<= 5 and self.mode == EDIT 
                and touch.state == MOVING then
                    self.width = self.width+touch.deltaX
            end
            if _mode == NEW_LINE and _startobj ~= self then
                Connector(_startobj,_startpoint,self,3)
                _mode = nil
                _startobj = nil
                _startpoint = nil
            end 
        else
            if self.mode == EDIT and touch.state == MOVING then
                local temppoint 
                if v:dist(vec2(self.x+self.width/2,self.y))<= 10 then
                    temppoint = 2
                elseif v:dist(vec2(self.x-self.width/2,self.y))<= 10 then
                    temppoint = 1
                elseif v:dist(vec2(self.x,self.y+self.height/2))<= 10 then
                    temppoint = 4
                elseif v:dist(vec2(self.x,self.y-self.height/2))<= 10 then
                    temppoint = 3
                end
                if _mode == nil and temppoint then
                    _mode = NEW_LINE
                    _startobj = self
                    _startpoint = temppoint
                end
            end
            --click outside the object to return to normal mode
            if self.mode == EDIT then
                self.mode = NORMAL
            end
        end
    end
             
    
  • Posts: 76
    --Connector part 1/2
    
    LINE = 0
    ARROW_NONE = 1
    ARROW_START = 2
    ARROW_END = 3
    ARROW_BOTH = 4
    LINE_STRAIGHT = 1
    LINE_CURVE = 2
    
    Connector = class()
    
    function Connector:init(s1,p1,s2,p2)
        -- you can accept and set parameters here
        self.shape1 = s1
        self.point1 = p1
        self.shape2 = s2
        self.point2 = p2
        self.type = LINE
        self.lineType = LINE_CURVE
        self.color = color(0, 0, 255, 255)
        self.strokeWidth = 5
        self.arrowType = ARROW_END
        self.points = {}
        self.mode = NORMAL
        World():add(self)
    end
    
    function Connector:draw()
        -- Codea does not automatically call this method
        local shape1 = self.shape1
        local shape2 = self.shape2
        local point1 = self.point1
        local point2 = self.point2
        local x1,y1,x2,y2
        x1,y1 = self:getPoint(shape1,point1)
        local p1 = vec2(x1,y1)
        local lowdist = 0
        for i = 1,4 do
            x2,y2 = self:getPoint(shape2,i)
            dist = p1:dist(vec2(x2,y2))
            if  dist<lowdist or lowdist==0 then
                lowdist = dist
                point2 = i
            end
        end
        x2,y2 = self:getPoint(shape2,point2)
        local p2 = vec2(x2,y2)
        local lowdist = 0
        for i = 1,4 do
            x1,y1 = self:getPoint(shape1,i)
            dist = p2:dist(vec2(x1,y1))
            if  dist<lowdist or lowdist==0 then
                lowdist = dist
                point1 = i
            end
        end
        x1,y1 = self:getPoint(shape1,point1)
        pushStyle()
            strokeWidth(self.strokeWidth)
            stroke(self.color)
            --Calculate points
            if (point1 == 1 or point1 == 2) and (point2 == 1 or point2 == 2) then
                local xm = math.floor((x2-x1)/2)
                self.points = {}
                if math.abs(y1-y2) <5 then
                    self.points[1] = vec2(x1,y1)
                    self.points[2] = vec2(x2,y2)
                else
                    self.points[1] = vec2(x1,y1)
                    self.points[2] = vec2(x1+xm,y1)
                    self.points[3] = vec2(x1+xm,y2) 
                    self.points[4] = vec2(x2,y2)
                end
            elseif (point1 == 3 or point1 == 4) and (point2 == 3 or point2 == 4) then
                local ym = math.floor((y2-y1)/2)
                self.points = {}
                if    math.abs(x1-x2)< 5 then
                    self.points[1] = vec2(x1,y1)
                    self.points[2] = vec2(x2,y2)
                else
                    self.points[1] = vec2(x1,y1)
                    self.points[2] = vec2(x1,y1+ym)
                    self.points[3] = vec2(x2,y1+ym) 
                    self.points[4] = vec2(x2,y2)
                end
            elseif (point1 == 1 or point1 == 2) and (point2 == 3 or point2 == 4) then
                self.points = {}
                self.points[1] = vec2(x1,y1)
                self.points[2] = vec2(x2,y1)
                self.points[3] = vec2(x2,y2) 
            elseif (point1 == 3 or point1 == 4) and (point2 == 1 or point2 == 2) then
                self.points = {}
                self.points[1] = vec2(x1,y1)
                self.points[2] = vec2(x1,y2)
                self.points[3] = vec2(x2,y2) 
            end
            --draw line        
            if self.lineType == LINE_STRAIGHT then
                for j = 1,#self.points-1 do
                    line(self.points[j].x,self.points[j].y,self.points[j+1].x,self.points[j+1].y)
                end
            else
                self:Spline()
            end
            --draw arrow
            if (self.arrowType == ARROW_START or self.arrowType == ARROW_BOTH)
                and point1 == 1 then
                line(x1,y1,x1-20,y1+10)
                line(x1,y1,x1-20,y1-10)
            end
            if (self.arrowType == ARROW_START or self.arrowType == ARROW_BOTH)
                and point1 == 2 then
                line(x1,y1,x1+20,y1+10)
                line(x1,y1,x1+20,y1-10)
            end
            if (self.arrowType == ARROW_START or self.arrowType == ARROW_BOTH)
                and point1 == 3 then
                line(x1-10,y1-20,x1,y1)
                line(x1+10,y1-20,x1,y1)
            end
            if (self.arrowType == ARROW_START or self.arrowType == ARROW_BOTH)
                and point1 == 4 then
                line(x1-10,y1+20,x1,y1)
                line(x1+10,y1+20,x1,y1)
            end
            if (self.arrowType == ARROW_END or self.arrowType == ARROW_BOTH)
                and point2 == 1 then
                line(x2-20,y2-10,x2,y2)
                line(x2-20,y2+10,x2,y2)
            end
            if (self.arrowType == ARROW_END or self.arrowType == ARROW_BOTH)
                and point2 == 2 then
                line(x2,y2,x2+20,y2+10)
                line(x2,y2,x2+20,y2-10)
            end
            if (self.arrowType == ARROW_END or self.arrowType == ARROW_BOTH)
                and point2 == 3 then
                line(x2-10,y2-20,x2,y2)
                line(x2+10,y2-20,x2,y2)
            end
            if (self.arrowType == ARROW_END or self.arrowType == ARROW_BOTH)
                and point2 == 4 then
                line(x2-10,y2+20,x2,y2)
                line(x2+10,y2+20,x2,y2)
            end
            if self.mode == EDIT then
                pushStyle()
                    strokeWidth(5)
                    stroke(127, 127, 127, 255)
                    fill(127,127,127, 255)
                    rectMode(CENTER)
                    rect(self.points[1].x,self.points[1].y,10,10)
                    rect(self.points[#self.points].x,self.points[#self.points].y,10,10)
                popStyle()
            end
        popStyle()
    end
    
  • Posts: 76
    --Connector part 2/2
    
    function Connector:getPoint(s,n)
        local x,y 
        if n == 1 then
            x = s.x-s.width/2
            y = s.y
        elseif n == 2 then
            x = s.x+s.width/2
            y = s.y
        elseif n == 3 then
            x = s.x
            y = s.y-s.width/2
        elseif n == 4 then
            x = s.x
            y = s.y+s.width/2
        end
        return x,y
    end
    
    function Connector:touched(touch)
        -- Codea does not automatically call this method
        local points = self.points
        if touch.state == BEGAN then
            if #points == 2 then
                if (touch.x >= points[1].x-5 and touch.x <= points[2].x+5 and
                   touch.y >= points[1].y-5 and touch.y <= points[2].y+5) then
                    self.mode = EDIT
                elseif self.mode == EDIT then
                    self.mode = NORMAL
                end
            elseif #points == 3 then
                if (touch.x >= points[1].x-5 and touch.x <= points[2].x+5 and
                   touch.y >= points[1].y-5 and touch.y <= points[2].y+5) or
                   (touch.x >= points[2].x-5 and touch.x <= points[3].x+5 and
                   touch.y >= points[3].y-5 and touch.y <= points[3].y+5) then
                    self.mode = EDIT
                elseif self.mode == EDIT then
                    self.mode = NORMAL
                end
            elseif #points == 4 then
                if (touch.x >= points[1].x-5 and touch.x <= points[2].x+5 and
                   touch.y >= points[1].y-5 and touch.y <= points[2].y+5) or
                   (touch.x >= points[2].x-5 and touch.x <= points[3].x+5 and     
                   touch.y >= points[2].y-5 and touch.y <= points[3].y+5) or
                   (touch.x >= points[3].x-5 and touch.x <= points[4].x+5 and
                   touch.y >= points[3].y-5 and touch.y <= points[4].y+5) then
                    self.mode = EDIT
                elseif self.mode == EDIT then
                    self.mode = NORMAL
                end
            end
        end
    end
    
    function Connector:SplineAt(t)
        local invT = 1-t
        local p1,p2,p3,p4
        if #self.points==2 then
            p1 = self.points[1]
            p2 = self.points[1]
            p3 = self.points[2]
            p4 = self.points[2]
        elseif #self.points==3 then
            p1 = self.points[1]
            p2 = self.points[2]
            p3 = self.points[2]
            p4 = self.points[3]
        else
            p1 = self.points[1]
            p2 = self.points[2]
            p3 = self.points[3]
            p4 = self.points[4]
        end
        return (invT^3)*p1 +
               3*(invT^2)*t*p2 +
               3*invT*(t^2)*p3 +
               (t^3)*p4
    end
    
    function Connector:Spline()
        local n = 10
        
        local from = self:SplineAt(0)
        
        stroke(self.color)
        strokeWidth(self.strokeWidth-2)
        lineCapMode(SQUARE)
        noSmooth()
    
        for i = 1,n do
            local to = self:SplineAt(i/n)
            
            line(from.x, from.y, to.x, to.y)
            
            from = to
        end
    end
    
    
    
  • Posts: 2,820

    Thanks! That's awsome!

  • SimeonSimeon Admin Mod
    Posts: 5,190

    Incredible work, @sanit!

  • Posts: 202

    Jeez, found this some minutes after uploading a new version of the loveCodea wrapper.

    The version of the wrapper that's available at the moment runs the program if you remove the lines "clearOutput()", "stroke(stroke())" and "fill(fill())" (who expected such a thing? I didn't.).

    Nice work, really!

  • edited April 2012 Posts: 76

    @Codeslinger Thanks. I just copied the loveCodea wrapper and ran it with my diagram. It works the same as it does on iPad. Nice jobs.

Sign In or Register to comment.