Code covered by the BSD License  

Highlights from
Interactive Spline Region of Interest

image thumbnail

Interactive Spline Region of Interest

by

 

17 Mar 2009 (Updated )

An interactive, closed spline

splineroi
classdef splineroi < handle
    % Spline Region of Interest Class
    % Usage:
    % >> r = splineroi;
    % >> r.addNode % to add nodes to the spline
    % >> r.addNode
    % >> r.addNode
    %
    % Methods:
    %     splineRoi - Constructor
    %     addNode - add a set of nodes either as a list of points (Nx2
    %               matrix)
    %     deleteNode - delete one node
    %     
    %     
    %
    
    %  Copyright (c) 2009 Azim Jinha <azimjinha@gmail.com>
    %
    %  Permission to use, copy, modify, and distribute this software for any
    %  purpose with or without fee is hereby granted, provided that the above
    %  copyright notice and this permission notice appear in all copies.
    %
    %  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    %  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    %  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    %  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    %  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    %  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    %  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    properties
        lineColor='b' % Spline color property
        markerColor='r' % Spline control node marker color
        marker='s' % Spline control node marker shape
        doSplineChangedNotification = true
    end
    properties(SetAccess=private)
        parent
        splineNodes =[] % spline node
        nodeCount = 0 % number of nodes in spline
        hSpline =-1 % handle to spline line
        hPoint=[] % handle to node markers
    end
    
    methods
        function self = splineroi
            % SPLINEROI Create Spline Region of Interest object
            
        end
        
        function delete(self)
            % DELETE deletes a splineroi instance.
            if ishandle(self.hPoint)
                delete(self.hPoint)
            end
            if ishandle(self.hSpline)
                delete(self.hSpline);
            end
        end
        
        function set(self,varargin)
            % SET set instance properties
            proplist = fields(self);
            for n=1:2:length(varargin)
                tmp=proplist(strcmpi(proplist,varargin{n}));
                switch length(tmp)
                    case 0
                        msg = ['There is no "', varargin{n},'" property'];
                        error('splineroi:setPropertyChk', msg );
                    case 1
                        switch char(tmp)
                            case 'lineColor'
                                self.lineColor=varargin{n+1};
                                if ishandle(self.hSpline), set(self.hSpline,'color',self.lineColor); end
                            case 'markerColor'
                                self.markerColor=varargin{n+1};
                                if ishandle(self.hPoint),
                                    set(self.hPoint,'markerEdgeColor',self.markerColor, ...
                                        'markerFaceColor',self.markerColor);
                                end
                            case 'marker'
                                self.marker=varargin{n+1};
                                if ishandle(self.hPoint)
                                    set(self.hPoint,'marker',varargin{n+1});
                                end
                            otherwise
                                error('splineroi:setReadOnlyProp', ...
                                    ['Attempt to modify readonly property "' varargin{n}, '".']);
                        end
                end
            end
        end
        
        function self=addNode(self,pt)
            % FUNCTION addNode
            % add N nodes to spline
            %
            % addNode(point) adds the point=[x,y]
            % to the spline where point has size nx2
            %
            % addNode with out arguments uses ginput
            % to add points.
            self.plot
            if nargin<2, [pt(1,1),pt(1,2)]=ginput(1); end
            
            if size(pt,2)~=2, error('input should be an nx2 matrix'); end
            
            for i =1:size(pt,1)
                self.splineNodes=[self.splineNodes;pt(i,1:2)];
                self.nodeCount = self.nodeCount+1;
                self.hPoint(self.nodeCount) = -1;
            end
            %             self.plotNode(self.nodeCount);
            %             self.plotSpline;
            self.plot
        end
        
        function deleteNode(self)
            [x,y,btn]=ginput(1);
            
            if self.nodeCount <3, return; end % don't delete nodes if there are less than 3
            if ~ishandle(self.hSpline), self.plot; end
            
            if ~isempty(btn)
                dist =inf;
                ind=0;
                for i=1:self.nodeCount
                    tmpDist = norm([x,y]-self.splineNodes(i,:));
                    if tmpDist<dist
                        dist=tmpDist;
                        ind = i;
                    end
                end
                
                
                switch ind
                    case 0
                        error('no nodes found');
                    case 1                        
                        self.splineNodes=self.splineNodes(2:end,:);
                        hP = self.hPoint(ind);
                        self.hPoint = self.hPoint(1:end-1);
                        
                    case self.nodeCount
                        self.splineNodes = self.splineNodes(1:end-1,:);
                        hP = self.hPoint(ind);
                        self.hPoint = self.hPoint(1:end-1);
                    otherwise
                        self.splineNodes = [self.splineNodes(1:ind-1,:);
                        self.splineNodes(ind+1:end,:)];
                        hP = self.hPoint(ind);
                        self.hPoint = [self.hPoint(1:ind-1),self.hPoint(ind+1:end)];
                        
                        
                end
                self.nodeCount = self.nodeCount-1;
                delete(hP)
                
                
                
                self.plot
            end
        end
        
        function self=plot(self)
            % FUNCTION plot
            % generate a plot of the spline in the current axes
            if isempty(self.parent), self.parent = -1; end
            if ~ishandle(self.parent)
                self.parent = axes;
                set(self.parent,'nextplot','add')
            end
            
            if isempty(self.hSpline)
                self.hSpline = -1;
            end
            
            
            if self.nodeCount>0
                self.plotSpline;
            end
            
            
            if isempty(self.hPoint)
                if self.nodeCount>0
                    self.hPoint = -1*ones(self.nodeCount,1);
                end
            end
            
            for i=1:self.nodeCount
                self.plotNode(i);
            end
        end
        
        function sp=calcSpline(self)
            % FUNCTION calcSpline
            % Calculates points on the spline
            if self.nodeCount>2
                splineFun=cscvn([self.splineNodes' self.splineNodes(1,:)']);
                sp=fnplt(splineFun);
            else
                sp = self.splineNodes';
            end
        end
        
        
    end
    
    methods(Access=private)
        function self=plotNode(self,iNode)
            % FUNCTION plotNode
            % Plots one node and sets properties to default values
            if ~ishandle(self.hPoint(iNode))
                hNew = plot(self.splineNodes(iNode,1),self.splineNodes(iNode,2),'rs');
                self.hPoint(iNode) = hNew;
                set(self.hPoint(iNode),'markerfacecolor',self.markerColor,'markerEdgeColor',self.markerColor,'Marker',self.marker);
                set(self.hPoint(iNode),'buttondownfcn',{@splineroi.animator,self,'start'});
            end
        end
        
        function self=plotSpline(self)
            % FUNCTION plotSPline
            % Plots the spline
            sPoints = self.calcSpline;
            if ishandle(self.hSpline)
                set(self.hSpline,'xdata',sPoints(1,:),'ydata',sPoints(2,:))
            else
                self.hSpline=plot(sPoints(1,:),sPoints(2,:),'color',self.lineColor,'lineStyle','-');
                set(self.hSpline,'hittest','off');
            end
        end
    end
    
    methods(Static)
        function animator(src,eventdata,self,action) %#ok<INUSL>
            % FUNCTION animator
            % Adds drag and drop functionality to spline nodes
            switch(action)
                case 'start'
                    set(gcbf,'WindowButtonMotionFcn',{@splineroi.animator,self,'move'});
                    set(gcbf,'WindowButtonUpFcn',{@splineroi.animator,self,'stop'});
                    self.doSplineChangedNotification = false;
                case 'move'
                    currPt=get(gca,'CurrentPoint');
                    set(gco,'XData',currPt(1,1));
                    set(gco,'YData',currPt(1,2));
                    self.splineNodes(self.hPoint==gco,:)=currPt(1,1:2);
                    self.plotSpline;
                case 'stop'
                    set(gcbf,'WindowButtonMotionFcn','');
                    set(gcbf,'windowButtonUpFcn','');
                    self.doSplineChangedNotification = true;
                    notify(self,'splineChanged');
            end%switch
        end
    end
    events
        splineChanged
    end
end

Contact us