Howdy, Stranger!

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

In this Discussion

Image distortion

edited June 2012 in Code Sharing Posts: 6

Clumsy attempt to use a grid mesh to distort an image.

Main (part 1 of 3)

dothidden=false

function setup()
    saveProjectInfo("Description","Distort picture using a grid mesh")
    
    print("2 finger tap --- toggle full screen")
    print("1 finger tap --- toggle control points")
    print("Move Points to distort")
    
    touches=0
    gesture=0
    
    -- pFIXBORDERS=false 
    FIXBORDERS = false -- Border points are movable
    
    pnX=1 -- n columns
    iparameter("nX",2,16,4)
    pnY=1 -- n rows
    iparameter("nY",2,16,4)
    
    pstate=0 -- cumulate distortion
    iparameter("Savestate",0,1,0)
    
    iparameter("DoWave",0,1,0) -- apply saw effect
    iparameter("HWave",0,2,0) -- horz 0, vert 1, both 2
    
    iparameter("DoReset",0,1,0) -- reset to prior saved state
    
    ImgW=0
    ImgH=0
    theMesh=mesh()
    ActivePoint=nil
    ActiveId=-1   
    CpGrid={} -- control points
    CpRect={} -- grid cells
    
    -- LOAD YOUR OWN HERE
    -- local BaseImg = readImage("Planet Cute:Icon")
    local BaseImg =  CreateMyImage(600,600)
    
    theMesh.texture=BaseImg
    ImgW=BaseImg.width
    ImgH=BaseImg.height
    
    OrigW=ImgW
    OrigH=ImgH
    
    local Tw,Th,Zom=ResizeToFit(ImgW,ImgH,WIDTH,HEIGHT)
    ImgW=Tw
    ImgH=Th
    CreateControlPoints(nX,nY)  
    CreateCells(nX,nY)
    PW=WIDTH
    PH=HEIGHT
end


function touched(touch)
    
    if touch.state == BEGAN then 
        if touches == 0 then gesture=1 end
        touches = touches +  1 
        end
        
    if (not dothidden) and touch.state==BEGAN and touches==1 and ActiveId == -1 then 
        ActivePoint=nil
        local found=false
        for i=1,nX+1 do
            if found == false then 
                for j=1,nY+1 do
                if found == false then 
                    local z = CpGrid[i][j]
                    local dd = (touch.x - z.c.x) ^ 2 + (touch.y - z.c.y) ^ 2
                    if dd < 300 then 
                        found=true
                        if CpGrid[i][j].movable then ActivePoint = CpGrid[i][j] end
                    end
                    end --endif
                end -- end j
            end -- endif
        end -- i
        if ActivePoint ~= nil then ActiveId=touch.id end   
    end
    
    if touch.state==MOVING and ActiveId==touch.id and ActivePoint ~= nil then 
        ActivePoint.c.x=touch.x
        ActivePoint.c.y=touch.y
        end
        
    if touch.state==ENDED then 
        if ActivePoint==nil and touches == 1 and gesture==1 then
            dothidden = (not dothidden)
            gesture=0
            end
         if ActivePoint == nil and touches==2 and gesture==1 then
            local tmode =displayMode()
            if tmode==STANDARD then
                displayMode(FULLSCREEN)
                else
                displayMode(STANDARD)
                end
            gesture=0
            end
            
        touches = touches - 1
        if touches==0 then gesture=0 end
        ActivePoint=nil
        ActiveId=-1
        end
        
end -- function
    
function ResetPoints()      
    for i=1,nX+1 do
        for j=1,nY+1 do
            local tobj = CpGrid[i][j]
            local tx=tobj.i.x * ImgW
            local ty=tobj.i.y * ImgH
            if tobj.movable then
                tobj.c.x=tx
                tobj.c.y=ty
                end
        end
    end
end
        
function draw()
    
    -- Resize picture for orientation or FullScreen
    if PW ~= WIDTH or PH~=HEIGHT then
       resetSize()
       PW=WIDTH
       PH=HEIGHT
       end
        
    -- change of rows columns 
    if pnX ~= nX or pnY ~= nY then
        InitGrids()
        pnX=nX
        pnY=nY
        end
    
    -- reset to last saved state    
    if DoReset==1 then
        DoReset=0
        ResetPoints()
        end
        
    -- apply saw to control points    
    if DoWave == 1 then
        Dowave=0
        local inc = 0.50
        for i=1,nX+1 do
        for j=1,nY+1 do
            
            local tobj = CpGrid[i][j]
            local tx=tobj.c.x
            local ty=tobj.c.y
            
            if HWave == 0 and nX > 3 and nY > 3 then -- horizontal
                if i%2 == 0 and j > 1 then ty = ty + inc end
            end
            if HWave == 1 and nX > 3 and nY > 3 then -- vertical
                if j%2 == 0 and i > 1 then tx = tx + inc end
            end
            if HWave == 2 and nX > 3 and nY > 3 then -- both
                if i%2 == 0 then tx = tx + inc end
                if j%2 == 0 then ty = ty + inc end
            end
            
            if tx < 0 then tx = 0 end
            if tx > WIDTH then tx = WIDTH end
            if ty < 0 then ty = 0 end
            if ty > HEIGHT then ty = HEIGHT end
            
            if tobj.movable then
                tobj.c.x=tx
                tobj.c.y=ty
                end 
            end
        end
        
        DoWave=0
        end
    
    if Savestate ~= pstate and Savestate == 1 then
        ApplyAndContinue()
        InitGrids()
        Savestate=0
        pstate=Savestate
        end
    
    background(40, 40, 40)
    fill(255,255,255,255)

    for i=1,nX do
        for j=1,nY do
            local z = CpRect[i][j]
            z:draw(theMesh)
        end
    end
    
    for i=1,nX+1 do
    for j=1,nY+1 do
    local z = CpGrid[i][j]
    z:draw()
    end
    end
    
end
Tagged:

Comments

  • Posts: 6

    Helpers (part 2 of 3)



    function CreateMyImage(imgw,imgh)          local myImg=image(imgw,imgh)          pushStyle()     setContext(myImg)     fill(0,0,0,255)     noStroke()          rect(1,1,imgw,imgh)          local tcol     local samp2     local div1 = imgw/8     local div2 = imgh/8                    for i=1,8 do         for j=1,8 do         samp2 = (i-1) * 3 + j         samp2=samp2%9                  if samp2 == 1 then tcol=color(255,0,0,255)         elseif samp2 == 2 then tcol = color(255,255,0,255)         elseif samp2 == 3 then tcol = color(0,255,0,255)         elseif samp2 == 4 then tcol = color(0,255,255,255)         elseif samp2 == 5 then tcol = color(0,0,255,255)         elseif samp2 == 6 then tcol = color(255,0,255,255)         elseif samp2 == 7 then tcol = color(255,255,255,255)         elseif samp2 == 8 then tcol = color(128,128,128,255)         else tcol = color(0,0,0,255)         end          fill(tcol)         rect((i-1)*div1+1,(j-1)*div2+1,div1,div2)         end -- j     end -- i     setContext()     popStyle()     return myImg end function resetSize()     local Tw,Th,Zom=ResizeToFit(OrigW,OrigH,WIDTH,HEIGHT)     local oldW=ImgW     local oldH=ImgH     ImgW=Tw     ImgH=Th     -- reset points     for i=1,nX+1 do         for j=1,nY+1 do             local z = CpGrid[i][j]             z.c.x = z.c.x / oldW * ImgW             z.c.y = z.c.y / oldH * ImgH         end -- j     end -- i end function InitGrids()     ActivePoint=nil     ActiveId=-1        CpGrid={}     CpRect={}     CreateControlPoints(nX,nY)       CreateCells(nX,nY) end -- Save the distort so far and resets the mesh      function ApplyAndContinue()     local hid=dothidden     dothidden=true     local minx=5000     local maxx=-5000     local miny=5000     local maxy=-5000     for i=1,nX+1 do     for j=1,nY+1 do         local z = CpGrid[i][j]         if z.c.x <= minx then minx=z.c.x end         if z.c.y <= miny then miny=z.c.y end         if z.c.x >= maxx then maxx=z.c.x end         if z.c.y >= maxy then maxy=z.c.y end         end -- j         end -- i     -- print(minx,miny,maxx,maxy)     local www=maxx-minx -- +1     local hhh=maxy-miny -- +1     local tmpimg=image(www,hhh)     setContext(tmpimg)     pushStyle()     fill(40,40,40,255)     noStroke()     noSmooth()     rect(0,0,www,hhh)     popStyle()     fill(255,255,255,255)     for i=1,nX do     for j=1,nY do         local z = CpRect[i][j]         z:draw(theMesh)         end         end     setContext()     dothidden=hid     ImgW=www     ImgH=hhh     theMesh.texture=tmpimg     OrigW=ImgW     OrigH=ImgH     local Tw,Th,Zom=ResizeToFit(ImgW,ImgH,WIDTH,HEIGHT)     ImgW=Tw     ImgH=Th     end               function CreateControlPoints(nX,nY)            CpRect={}     CpGrid={}          for i=1,nX+1 do         local stari = (i-1) / nX * ImgW         if stari > ImgW then stari = ImgW end         CpGrid[i]={}         for j=1,nY+1 do             local starj =  (j-1) / nY * ImgH             if starj > ImgH then starj = ImgH end             CpGrid[i][j]=ControlPoint(stari,starj,stari/ImgW,starj/ImgH)             if FIXBORDERS then                 if i==1 or i==nX+1 or j==1 or j==nY+1 then                     CpGrid[i][j].movable=false                 end             end         end     end end    function CreateCells(nX,nY)          CpRect={}       for i=1,nX do         CpRect[i]={}         for j=1,nY do             local tl = CpGrid[i][j]             local tr = CpGrid[i+1][j]             local bl = CpGrid[i][j+1]             local br = CpGrid[i+1][j+1]             CpRect[i][j]=ControlRect(tl,tr,bl,br)         end     end end     function ResizeToFit(cxDib,cyDib,ScrW,ScrH)              local neww= ScrW         local newh =ScrH         local ratio = cxDib / cyDib                  newh = neww  / ratio                  if(newh > ScrH) then              ratio = ScrH / newh             newh = ScrH             neww = neww * ratio             if(neww > ScrW) then neww=ScrW end         end         local zoomw= neww/ cxDib         return neww,newh,zoomw end -- thepoints : array of vec2 (closed shape, dup the first point) -- test      : vec2 -- point to be tested function PointInPoly(thepoints,test)   local nvert = #thepoints   local c = 0      local j=nvert   local z0   local z1      for i=1,nvert do        z0 = thepoints[i]     z1 = thepoints[j]          if ( ((   z0.y > test.y) ~= ( z1.y > test.y)) and      (test.x <  (z1.x-z0.x) * (test.y-z0.y) / (z1.y-z0.y) + z0.x) ) then        if c == true then               c = false        else           c = true        end     end      j=i     end -- loop          return c      end -- Dist from point p to line p1,p2 (segment) -- Returns distance and closest point on the line function DistFromSegment(p,p1,p2)     local dx = p2.x - p1.x     local dy = p2.y - p1.y     local Nearpt = p1     local dist=0          if dx == 0 and dy == 0 then         -- It's a point not a line segment.         dx = p.x - p1.x         dy = p.y - p1.y         Nearpt = p1         dist = math.sqrt(dx * dx + dy * dy)         return dist,Nearpt     end     -- Calculate the t that minimizes the distance.     local t = ((p.x - p1.x) * dx + (p.y - p1.y) * dy) / (dx * dx + dy * dy)     if t < 0 then         dx = p.x - p1.x         dy = p.y - p1.y         Nearpt = p1     elseif t > 1 then         dx = p.x - p2.x         dy = p.y - p2.y         Nearpt = p2     else         Nearpt = vec2( p1.x + t * dx, p1.y * t * dy)         near_y = Y1 + t * dy         dx = p.x - Nearpt.x         dy = p.y - Nearpt.y     end     dist = math.sqrt(dx * dx + dy * dy)     return dist,Nearpt end function pointToLineDistance(A, B, P)     local normalLength = math.sqrt((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y))     local distance=0     if normalLength > 0.000001 then           distance = math.abs((P.x - A.x) * (B.y - A.y) - (P.y - A.y) * (B.x - A.x)) / normalLength     else         distance = math.sqrt((P.x-A.x)*(P.x-A.x)+(P.y-A.y)*(P.y-A.y))     end      return distance end
  • Posts: 6

    Control Points (part 3 of 3)

    ControlPoint = class()
    
    function ControlPoint:init(x,y,x1,y1)
        self.c=vec2(x,y) -- in pixels
        self.i=vec2(x1,y1) -- corresp image location .. in pct of width and height
        self.movable=true
    end
    
    function ControlPoint:draw() 
        if not dothidden then
        pushStyle()
        strokeWidth(2)
        stroke(209, 63, 63, 255)  
        fill(194, 154, 60, 255)
        ellipse(self.c.x,self.c.y, 10, 10)
        popStyle()
        end
        
    end
    
    ControlRect=class()
    
    function ControlRect:init(tl,tr,bl,br)
        self.tl= tl
        self.tr= tr
        self.bl= bl
        self.br= br
        -- rectangle always
        local cx = (tl.i.x + tr.i.x + bl.i.x + br.i.x) / 4
        local cy = (tl.i.y + tr.i.y + bl.i.y + br.i.y) / 4
        self.centeri = vec2(cx,cy)
        
    end
    
    function ControlRect:draw(theMesh)
        
        -- create 4 triangles by adjusting center point
    
        local verts={}
        local tverts={}
        
        local cx = (self.tl.c.x + self.tr.c.x + self.bl.c.x + self.br.c.x) / 4
        local cy = (self.tl.c.y + self.tr.c.y + self.bl.c.y + self.br.c.y) / 4
        local centerc = vec2(cx,cy)
        
        -- Centroid : shape is not necessarily a rectangle 
        local sarea=0
        local ax=0
        local ay=0
        
        local a1 =  (self.tl.c.x * self.tr.c.y) - (self.tl.c.y * self.tr.c.x) 
        sarea = sarea + a1
        ax = ax +   (self.tl.c.x   + self.tr.c.x) * a1
        ay = ay +   (self.tl.c.y   + self.tr.c.y) * a1
        
        a1 =  (self.tr.c.x * self.br.c.y) - (self.tr.c.y * self.br.c.x) 
        sarea = sarea + a1
        ax = ax +   (self.tr.c.x   + self.br.c.x) * a1
        ay = ay +   (self.tr.c.y   + self.br.c.y) * a1
        
        a1 =  (self.br.c.x * self.bl.c.y) - (self.br.c.y * self.bl.c.x) 
        sarea = sarea + a1
        ax = ax +   (self.br.c.x   + self.bl.c.x) * a1
        ay = ay +   (self.br.c.y   + self.bl.c.y) * a1
        
        a1 =  (self.bl.c.x * self.tl.c.y) - (self.bl.c.y * self.tl.c.x) 
        sarea = sarea + a1
        ax = ax +   (self.bl.c.x   + self.tl.c.x) * a1
        ay = ay +   (self.bl.c.y   + self.tl.c.y) * a1
        sarea = sarea * 0.50
        
        if sarea ~= 0 then
           ax = ax / ( 6 * sarea)
           ay = ay / ( 6 * sarea)
           centerc=vec2(ax,ay)
           end
        
        --right
        table.insert(verts,self.tr.c)
        table.insert(tverts,self.tr.i)
        table.insert(verts,self.br.c)
        table.insert(tverts,self.br.i)
        table.insert(verts,centerc)
        table.insert(tverts,self.centeri)
    
        
        -- Bottom
        table.insert(verts,self.tr.c)
        table.insert(tverts,self.tr.i)
        table.insert(verts,centerc)
        table.insert(tverts,self.centeri)
        table.insert(verts,self.tl.c)
        table.insert(tverts,self.tl.i)
        
        --left
        table.insert(verts,centerc)
        table.insert(tverts,self.centeri)
        table.insert(verts,self.bl.c)
        table.insert(tverts,self.bl.i)
        table.insert(verts,self.tl.c)
        table.insert(tverts,self.tl.i)
        
        --top
        table.insert(verts,self.br.c)
        table.insert(tverts,self.br.i)
        table.insert(verts,self.bl.c)
        table.insert(tverts,self.bl.i)
        table.insert(verts,centerc)
        table.insert(tverts,self.centeri)
        theMesh.vertices = verts
        theMesh.texCoords = tverts
        pushStyle()
        fill(255,255,255,255) -- otherwise applies tint to image
        theMesh:draw()
        popStyle()
        -- self:drawCenters(centerc) -- for debug
        end
        
    function ControlRect:drawCenters(centerc) 
        pushStyle()
        strokeWidth(2)
        stroke(209, 63, 63, 255)  
        noFill()
        line(self.tl.c.x,self.tl.c.y,self.tr.c.x,self.tr.c.y)
        line(self.tr.c.x,self.tr.c.y,self.br.c.x,self.br.c.y)
        line(self.br.c.x,self.br.c.y,self.bl.c.x,self.bl.c.y)
        line(self.bl.c.x,self.bl.c.y,self.tl.c.x,self.tl.c.y)
        
        fill(0,255,255,255)
        strokeWidth(2)
        stroke(209, 63, 63, 255)  
        ellipse(centerc.x,centerc.y, 10, 10)
        popStyle()
    end
    
  • Posts: 2,820

    This is actually pretty cool! Worth continuing. Good job!

Sign In or Register to comment.