-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbezierdraw.m
executable file
·304 lines (276 loc) · 12.6 KB
/
bezierdraw.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
function [h,x,y]=bezierdraw(varargin)
% BEZIERDRAW Draw curved line by mouseclicks
% Reminiscent of bezier-line drawing in Coreldraw. Simply click in plot with
% mouse to draw your line, alternating between making nodes (button 1), and
% aligning directional constrainment (button 2):
%
% [h,x,y] = bezierdraw;
%
% Input = parameter/value pairs to specify properties of the line
% (default is a solid, black line)
%
% h = handle to drawn line object
% x,y = coordinate-vectors for the line
%
% Mouse-button 1 : Create node
% Mouse-button 2 (Control-B1) : Align "ruler". Click on a point "after"
% the node. Create nodes and create rulers
% alternately. Press same button again to
% redo last action.
% ESC-key : Erase nodes backwards
% Any other key : Quit
%
% ARROWS (requires function ARROW): If the string 'arrow' or any of the
% ARROW PROPERTIES is added to the parameter/value pair input, an arrowhead
% will be added to the line. Any parameter/value pairs for line, patch and
% ARROW can be input. Only 'width' or 'linewidth' is needed. ARROW is part
% of MATDRAW which can be obtained at
% ftp://ftp.mathworks.com/pub/contrib/v5/graphics/matdraw/ . Alternatively,
% the x and y output from BEZIERDRAW can be used as inputs to Tore Furevik's
% MY_ARROW.
%
% M_MAP users: If you want lon/lat coordinates to save the geographical
% lines, use M_XY2LL on the output x and y.
%
% The bezier-algorithm was kindly provided by Nicolas de Dreuille
% ([email protected]) with explanation on
% http://servasi.insa-rouen.fr/~ndreuill/projetananum/
%
% See also ARROW GINPUT M_MAP
%Time-stamp:<Last updated on 02/06/13 at 11:49:55 by [email protected]>
%File:<d:/home/matlab/bezierdraw.m>
for i=1:length(varargin) % Ensure all lower case input
varargin{i}=lower(varargin{i});
end
%----------------------------------------------------------------------
x=find(strcmp(varargin,'xdata'))+1;
y=find(strcmp(varargin,'ydata'))+1;
if any(x)&any(y)
x=varargin{x}; y=varargin{y};
h=line('xdata',x,'ydata',y);
else % no data input means
[h,x,y]=draw; % drawing by hand
end
%----------------------------------------------------------------------
if ~any(strcmp(varargin,'color')) % Default styles
varargin=[varargin,{'color'},{'k'}];end
if ~any(strcmp(varargin,'linestyle'))
varargin=[varargin,{'linestyle'},{'-'}];end
find(strcmp(varargin,'width'))+1; % Lineproperties
if any(ans)
varargin=[varargin,{'linewidth'},varargin(ans)];
end
lineproperties=propertiesfor('line',varargin{:});
if ~isempty(lineproperties)
set(h(1),lineproperties{:});
end
% Arrow:
if any(strcmp(varargin,'arrow'))|~isempty(propertiesfor('arrow',varargin{:}))
find(strcmp(varargin,'color'))+1; % Default colors
if ~any(strcmp(varargin,'edgecolor'))
varargin=[varargin,{'edgecolor'},varargin(ans)]; end
if ~any(strcmp(varargin,'facecolor'))
varargin=[varargin,{'facecolor'},varargin(ans)]; end
find(strcmp(varargin,'linewidth'))+1; % Default length
if ~any(strcmp(varargin,'length'))&any(ans)
varargin=[varargin,{'length'},{varargin{ans}*6}]; end
varargin(find(strcmp(varargin,'linewidth')))={'width'}; % Width
arrowproperties=[propertiesfor('arrow',varargin{:}),...
propertiesfor('patch',varargin{:})];
h(2)=arrow([x(end-1) y(end-1)],[x(end) y(end)],...
'tag','bezierdraw',arrowproperties{:}); % Plot arrow
% Move arrowhead to end of line (6th point is tail of arrow):
xt=get(h(2),'xdata'); xs=(xt(3)+xt(9))/2;
dx=x(end)-xs; xt=xt([1:3 9:11])+dx;
yt=get(h(2),'ydata'); ys=(yt(3)+yt(9))/2;
dy=y(end)-ys; yt=yt([1:3 9:11])+dy;
% and removed body of arrow
%
% Merge line into arrow-patch:
% w=complex(xt(4)-xt(5),yt(4)-yt(5)); % direction of xt-counting
% a=complex(xt(1)-xt(5),yt(1)-yt(5));
% %keyboard
% W=abs(w)*sign((angle(w)-angle(a)))
% %complex((xt(4)-xt(5)),(yt(4)-yt(5))); W=abs(ans)*sign(angle(ans));
% clear i
% dx=diff(x); dy=diff(y);
% phi=angle(dx+i*dy);
% dx=-W*sin(phi); dy=W*cos(phi);
% x1=x(1:end-1)+dx; y1=y(1:end-1)+dy;
% x2=x(1:end-1)-dx; y2=y(1:end-1)-dy;
% xt=[xt(1:3)',fliplr(x1),x2,xt(9:11)'];
% yt=[yt(1:3)',fliplr(y1),y2,yt(9:11)'];
set(h(2),'xdata',xt,'ydata',yt);
%
% delete(h(1)); h=h(2);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [h,x,y]=draw()
poi=get(gcf,'pointer'); set(gcf,'pointer','cross'); % figure presets
xmod=get(gca,'xlimmode'); ymod=get(gca,'ylimmode');
set(gca,'xlimmode','manual','ylimmode','manual');
[X,Y,NX,NY,xx,yy,nxx,nyy,hp,hab,nh]=deal([]); % Init variables
N=20;
% x,y Nodes
% X,Y Total points in order, both nodes and ruler-ends [a,x,b]
% NX,NY Last three/new total points
% xx,yy Main curve
% nxx,nyy Last/new curve segment
k=waitforbuttonpress; % First point
button='normal';
get(gca,'currentpoint'); x=ans(1,1);y=ans(1,2);
% Init plots
h=line(x,y,'tag','bezierdraw'); % :main curve
nh=line(x,y,'marker','.'); % :last segment
hp=line(x,y,'linestyle','none','marker','o','color','r'); % :red nodes
hab=line(x,y,'linestyle',':','marker','.','color','g'); % :green ruler
set([h,nh,hab],'xdata',[],'ydata',[]); % only node visible
k=waitforbuttonpress; % Next point
oldbutt=button; button=get(gcf,'selectiontype');
get(gca,'currentpoint'); px=ans(1,1);py=ans(1,2);
while ~k % LOOP:
switch button
case 'normal' % NODE:
if strcmp(oldbutt,button)
x(end)=px; y(end)=py; % Just a changed node
else
x=[x,px]; y=[y,py]; % New node after ruler
X=[X,NX]; Y=[Y,NY]; % Update data
xx=[xx,nxx]; yy=[yy,nyy]; %
nxx=[]; nyy=[]; %
set(h,'xdata',xx,'ydata',yy); % Update main curve
end
set(hp,'xdata',x,'ydata',y); % Update node
case 'alt'
bx=px; by=py; % RULER:
ax=[x(end)-(bx-x(end))]; ay=[y(end)-(by-y(end))];% Find ruler data
set(hab,'xdata',[ax bx],'ydata',[ay by]); % Plot ruler
%
NX=[ax,x(end),bx]; NY=[ay,y(end),by]; % Last three total
if length(X)>2 % Create new segment
x4=[X(end-1:end) NX(1:2)]; y4=[Y(end-1:end) NY(1:2)]; %
courbeb([x4;y4],N+1); nxx=ans(1,2:end); nyy=ans(2,2:end); %
set(nh,'xdata',nxx,'ydata',nyy); %
end %
end
k=waitforbuttonpress;
if k==1 & strcmp(char(27),get(gcf,'currentcharacter'))% ESC PRESSED:
if length(x)>1 % erase one point back
button='alt';
px=X(end); py=Y(end);
X=X(1:end-3); Y=Y(1:end-3);
nxx=[]; nyy=[];
xx=xx(1:end-N); yy=yy(1:end-N);
x=x(1:end-1); y=y(1:end-1);
else % erase first point = return to scratch
button='normal';
[X,Y,NX,NY,xx,yy,nxx,nyy]=deal([]);
x=x(1); y=y(1);
set(hab,'xdata',[],'ydata',[]);
px=x; py=y; % Next loop is
oldbutt=button; % just an update
end
set(nh,'xdata',nxx,'ydata',nyy);
set(h,'xdata',xx,'ydata',yy);
set(hp,'xdata',x,'ydata',y);
k=0;
else % MOUSE CLICKED
oldbutt=button; button=get(gcf,'selectiontype'); % Next point
get(gca,'currentpoint'); px=ans(1,1);py=ans(1,2); %
end
end
delete(nh,hp,hab); % Reset figure
set(gcf,'pointer',poi);
set(gca,'xlimmode',xmod,'ylimmode',ymod);
xx=[xx,nxx]; yy=[yy,nyy]; % Format output
set(h,'xdata',xx,'ydata',yy);
x=xx; y=yy;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [MatBezier]=courbeb(Mat,N);
% [MatBezier] = CourbeB(Mat,N);
%
% Mat une matrice de deux vecteurs : celui des abscisses et celui des
% ordonnées.
% N le nombre de points à calculer pour chaque segment
%
% URL: http://servasi.insa-rouen.fr/~ndreuill/projetananum/
% Réalisé par Nicolas de Dreuille.
% E-mail : [email protected]
t=linspace(0,1,N);
for i=1:N
[xp,yp]=Bezier(Mat,t(i)); %2
xBezier(i)=xp;
yBezier(i)=yp;
end
MatBezier=[xBezier;yBezier];
% Bezier.m Permet de calculer un point de la courbe de Bézier
% définie par n points de contrôle
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%2
function [xpoint,ypoint]=Bezier(Mat,t);
x=Mat(1,:);
y=Mat(2,:);
for j=(length(x)-1):-1:1
for i=1:j
x(i)=(1-t)*x(i)+t*x(i+1);
y(i)=(1-t)*y(i)+t*y(i+1);
end
end
xpoint=x(1);
ypoint=y(1);
% Binterp.m : Permet d'interpoler un ensemble de points avex
% l'approximation de Bézier.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function out=propertiesfor(type,varargin)
% PROPERTIESFOR Extract relevant graphics object properties
% from a cellstring of arbitrary property/value pairs. This makes it
% possible to create functions with a global input of property/value
% pairs to be passed on to different types of graphics objects.
%
% out = propertiesfor(type,varargin)
%
% type = Object type string.
% varargin = Cellstring with arbitrary property/value-pairs from which to
% extract relevant pairs for this object type. Property-names
% must be complete!
% out = Cellstring with relevant property/value-pairs
%
% EXAMPLE: lineproperties = propertiesfor('line',varargin{:});
% set(h,lineproperties{:}); % h is handle to line-object
%
% INVERSE search for properties _not_ relevant for given object is
% possible with a '~' preceeding the object string (i.e. type='~line').
%
% Properties defined for all Matlab Graphics object types, as well as
% for properties of arrow
%
% See also HELPDESK -> Handle Graphics Objects
inverse=0;
if any(findstr('~',type)) % inverse or regular search
inverse=1; type=type(2:end);
end
type=lower(type);
props=get_props(type); % Define relevant properties
if isempty(varargin), out=varargin{:}, return; end
for i=1:length(varargin) % Ensure all lower case input
varargin{i}=lower(varargin{i});
end
for i=1:length(props) % Find matches
ii(i,:)=strcmp(varargin,props(i));
end
j=any(ii); % indices for relevant properties
if inverse
j=find(all(~[j;[0 j(1:end-1)]])); % indices for pairs not relevant
else
j=find(any([j;[0 j(1:end-1)]])); % indices for relevant pairs
end
out=varargin(j);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Define relevant properties %%%%
function props=get_props(type)
switch type
case 'line'
props={'color','erasemode','linestyle','linewidth','marker','markersize','markeredgecolor','markerfacecolor','xdata','ydata','zdata','buttondownfcn','children','clipping','createfcn','deletefcn','busyaction','handlevisibility','hittest','interruptible','parent','selected','selectionhighlight','tag','type','uicontextmenu','userdata','visible'};
case 'patch'
props={'cdata','cdatamapping','facevertexcdata','edgecolor','erasemode','facecolor','faces','linestyle','linewidth','marker','markeredgecolor','markerfacecolor','markersize','vertices','xdata','ydata','zdata','facelighting','edgelighting','backfacelighting','ambientstrength','diffusestrength','specularstrength','specularexponent','specularcolorreflectance','vertexnormals','normalmode','buttondownfcn','children','clipping','createfcn','deletefcn','busyaction','handlevisibility','hittest','interruptible','parent','selected','selectionhighlight','tag','type','uicontextmenu','userdata','visible'};
case 'arrow'
props={'start','stop','length','baseangle','tipangle','width','page','crossdir','normaldir','ends','objecthandles'};
end