|
1 | 1 | function vennfig = venn4(n,varargin)
|
2 |
| -% Draw venn diagram with two to four sets with optional text labels. |
| 2 | +%% Draw venn diagram with two to four sets with optional text labels. |
3 | 3 | % User can specify the number of sets to draw (maximum four) and label each
|
4 | 4 | % set and the intersectional regions between sets.
|
5 |
| -% |
6 |
| -% USAGE: |
7 |
| -% vennfig = venn4(n,varargin) |
| 5 | +% Man Ho Wong (2022). |
8 | 6 | %
|
9 |
| -% INPUTS: n [positive integer] |
| 7 | +% Input : n [positive integer] |
10 | 8 | % Number of sets to draw
|
11 | 9 | % sets [string | char | cellstr | numeric]
|
12 | 10 | % An array of set names in left-to-right order
|
13 | 11 | % labels [string | char | cellstr | numeric]
|
14 | 12 | % An array of label names for labeling each section;
|
15 |
| -% Elements in the array must follow the following order: |
| 13 | +% Elements in the array must follow the following order: |
16 | 14 | % For diagram with Set A and B, labels for 3 sections are
|
17 | 15 | % A, B and A&B.
|
18 | 16 | % For diagram with Set A, B and C, labels for 7 sections are
|
19 |
| -% A, B, C, D, A&B, A&C, B&C and A&B&C. |
| 17 | +% A, B, C, A&B, A&C, B&C and A&B&C. |
20 | 18 | % For diagram with Set A, B, C and D, labels for 15 sections
|
21 |
| -% are A, B, C, D, A&B, A&C, A&D, B&C, B&D, C&D, A&B&C, A&B&D |
| 19 | +% are A, B, C, D, A&B, A&C, A&D, B&C, B&D, C&D, A&B&C, A&B&D |
22 | 20 | % , A&C&D, B&C&D, A&B&C&D.
|
23 | 21 | % Any extra labels will be ignored.
|
24 | 22 | % colors [rows of RGB triplet]
|
|
27 | 25 | % If number of colors is less than n, colors will be
|
28 | 26 | % repeated.
|
29 | 27 | % alpha [0 to 1]
|
30 |
| -% Fill color transparency; 0 = fully transparent. |
| 28 | +% Fill color alpha; 0 = fully transparent. |
31 | 29 | % edgeC [RGB triplet]
|
32 | 30 | % Edge color (only effective when 'edgeW' is > 0).
|
33 | 31 | % edgeW [positive number]
|
34 | 32 | % Edge width (By default, there is no edge)
|
35 | 33 | % labelC [RGB triplet]
|
36 | 34 | % Color of section labels.
|
| 35 | +% fontSize [positive number] |
| 36 | +% Font size for the text (default is adjusted based on number of sets). |
37 | 37 | %
|
38 |
| -% OUTPUT : A Veenn diagram will be drawn on a new figure. |
| 38 | +% Output : A Veenn diagram will be drawn on a new figure. |
39 | 39 | % vennfig (optional): A handle to the figure.
|
40 | 40 | %
|
41 |
| -% EXAMPLES: see README.md |
42 |
| -% |
43 |
| -% Reference: |
44 |
| -% cff-version: 1.2.0 |
45 |
| -% ..Authors: |
46 |
| -% - family-names: Wong |
47 |
| -% given-names: Man Ho |
48 |
| -% orcid: https://orcid.org/0000-0002-3738-1914 |
49 |
| -% version: 1.0.0 |
50 |
| -% doi: 10.5281/zenodo.7297812 |
51 |
| -% date-released: 2022-11-07 |
| 41 | +% Examples: see README.md |
52 | 42 |
|
53 | 43 | % default set names
|
54 | 44 | s = repmat(" ",4,1); % white space as spaceholder
|
|
72 | 62 | addParameter(p,'edgeC', 'w', validColor);
|
73 | 63 | addParameter(p,'edgeW', [], validPosNum);
|
74 | 64 | addParameter(p,'labelC', 'k', validColor);
|
| 65 | +addParameter(p,'fontSize', [], validPosNum); % Optional fontSize input |
75 | 66 |
|
76 | 67 | % Parse input
|
77 | 68 | parse(p,varargin{:});
|
|
82 | 73 | edgeC = p.Results.edgeC;
|
83 | 74 | edgeW = p.Results.edgeW;
|
84 | 75 | labelC = p.Results.labelC;
|
| 76 | +fontSize = p.Results.fontSize; |
| 77 | + |
| 78 | +% Default font sizes based on the number of sets |
| 79 | +if isempty(fontSize) |
| 80 | + switch n |
| 81 | + case 2 |
| 82 | + fontSizeSets = 28; |
| 83 | + fontSizeIntersections = 24; |
| 84 | + case 3 |
| 85 | + fontSizeSets = 26; |
| 86 | + fontSizeIntersections = 22; |
| 87 | + case 4 |
| 88 | + fontSizeSets = 24; |
| 89 | + fontSizeIntersections = 20; |
| 90 | + otherwise |
| 91 | + error('n must be an integer between 2 and 4.'); |
| 92 | + end |
| 93 | +else |
| 94 | + % If user provides a fontSize, use it for both sets and intersections, scaled appropriately |
| 95 | + fontSizeSets = fontSize + 2; % Slightly larger for set labels |
| 96 | + fontSizeIntersections = fontSize; |
| 97 | +end |
85 | 98 |
|
86 | 99 | % repeat colors if number of colors given is less than n
|
87 | 100 | if height(colors) < n
|
|
120 | 133 | B = v(2);
|
121 | 134 | C = v(3);
|
122 | 135 | D = v(4);
|
123 |
| - |
| 136 | + |
124 | 137 | AB = v(5);
|
125 | 138 | AC = v(6);
|
126 | 139 | AD = v(7);
|
127 | 140 | BC = v(8);
|
128 | 141 | BD = v(9);
|
129 | 142 | CD = v(10);
|
130 |
| - |
| 143 | + |
131 | 144 | ABC = v(11);
|
132 | 145 | ABD = v(12);
|
133 | 146 | ACD = v(13);
|
134 | 147 | BCD = v(14);
|
135 |
| - |
| 148 | + |
136 | 149 | ABCD = v(15);
|
137 | 150 | end
|
138 | 151 |
|
|
154 | 167 | circle(X,Y,r,colors(1,:),alpha);
|
155 | 168 | circle(X+r,Y,r,colors(2,:),alpha);
|
156 | 169 | % draw circle A edge again (so it's not covered by circle B)
|
157 |
| - circle(X,Y,r,[0 0 0],0); |
| 170 | + circle(X,Y,r,[0 0 0],0); |
158 | 171 |
|
159 |
| - text(1,2.2,s(1),'HorizontalAlignment','right'); |
160 |
| - text(2,2.2,s(2),'HorizontalAlignment','left'); |
| 172 | + text(1,2.2,s(1),'HorizontalAlignment','right', 'FontSize', fontSizeSets, 'FontWeight', 'bold'); |
| 173 | + text(2,2.2,s(2),'HorizontalAlignment','left', 'FontSize', fontSizeSets, 'FontWeight', 'bold'); |
161 | 174 |
|
162 |
| - text(0.5,1,A,'HorizontalAlignment','center') |
163 |
| - text(2.5,1,B,'HorizontalAlignment','center') |
164 |
| - text(1.5,1,AB,'HorizontalAlignment','center') |
| 175 | + text(0.5,1,A,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); |
| 176 | + text(2.5,1,B,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); |
| 177 | + text(1.5,1,AB,'HorizontalAlignment','center', 'FontSize', fontSizeSets, 'FontWeight', 'bold'); |
165 | 178 |
|
166 | 179 | case 3
|
167 | 180 | xlim([-0.5 4])
|
168 | 181 | circle(X,Y,r,colors(1,:),alpha);
|
169 | 182 | circle(X+r,Y,r,colors(2,:),alpha);
|
170 | 183 | circle(X+r/2,Y+r,r,colors(3,:),alpha);
|
171 |
| - % draw circle A and B edge again (so they are not covered by circle C) |
172 |
| - circle(X,Y,r,[0 0 0],0); |
173 |
| - circle(X+r,Y,r,[0 0 0],0); |
174 | 184 |
|
175 |
| - text(1.5,3.2,s(1),'HorizontalAlignment','center') |
176 |
| - text(-0.1,1,s(2),'HorizontalAlignment','right') |
177 |
| - text(3.1,1,s(3),'HorizontalAlignment','left') |
| 185 | + text(1.5,3.2,s(1),'HorizontalAlignment','center', 'FontSize', fontSizeSets, 'FontWeight', 'bold'); |
| 186 | + text(-0.1,1,s(2),'HorizontalAlignment','right', 'FontSize', fontSizeSets, 'FontWeight', 'bold'); |
| 187 | + text(3.1,1,s(3),'HorizontalAlignment','left', 'FontSize', fontSizeSets, 'FontWeight', 'bold'); |
178 | 188 |
|
179 |
| - text(1.5,2.4,A,'HorizontalAlignment','center') |
180 |
| - text(0.5,1,B,'HorizontalAlignment','center') |
181 |
| - text(2.5,1,C,'HorizontalAlignment','center') |
| 189 | + text(1.5,2.4,A,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); |
| 190 | + text(0.5,1,B,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); |
| 191 | + text(2.5,1,C,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); |
182 | 192 |
|
183 |
| - text(1,1.75,AB,'HorizontalAlignment','center') |
184 |
| - text(1.5,0.75,BC,'HorizontalAlignment','center') |
185 |
| - text(2,1.75,AC,'HorizontalAlignment','center') |
186 |
| - |
187 |
| - text(1.5,1.4,ABC,'HorizontalAlignment','center') |
| 193 | + text(1,1.75,AB,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); |
| 194 | + text(1.5,0.75,BC,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); |
| 195 | + text(2,1.75,AC,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); |
| 196 | + |
| 197 | + text(1.5,1.4,ABC,'HorizontalAlignment','center', 'FontSize', fontSizeSets, 'FontWeight', 'bold'); |
| 198 | + |
| 199 | + case 4 |
188 | 200 |
|
189 |
| - case 4 |
190 | 201 | xlim([-3.5 4])
|
191 | 202 |
|
192 |
| - % ellipse A and B |
| 203 | + % Draw ellipses for the sets |
| 204 | + % Ellipse A and B |
193 | 205 | [X,Y] = getEllipse(0.8,1.6,[-1.1 1]);
|
194 |
| - patch(X,Y,colors(1,:),'FaceAlpha',alpha,'LineStyle','none'); |
195 |
| - patch(X+1,Y+0.5,colors(2,:),'FaceAlpha',alpha,'LineStyle','none'); |
| 206 | + patch(X,Y,colors(1,:),'FaceAlpha',alpha,'LineStyle','none'); % Set A |
| 207 | + patch(X+1,Y+0.5,colors(2,:),'FaceAlpha',alpha,'LineStyle','none'); % Set B |
196 | 208 |
|
197 |
| - % ellipse C and D |
| 209 | + % Ellipse C and D |
198 | 210 | [X,Y] = getEllipse(1.6,0.8,[1.1 1]);
|
199 |
| - patch(X-1,Y+0.5,colors(3,:),'FaceAlpha',alpha,'LineStyle','none'); |
200 |
| - patch(X,Y,colors(4,:),'FaceAlpha',alpha,'LineStyle','none'); |
201 |
| - |
202 |
| - % draw ellipse edges separately (so they are not covered by others) |
203 |
| - patch(X-1,Y+0.5,'w','FaceAlpha',0,'LineStyle','none'); % ellipse C |
204 |
| - patch(X,Y,'w','FaceAlpha',0,'LineStyle','none'); % ellipse D |
| 211 | + patch(X-1,Y+0.5,colors(3,:),'FaceAlpha',alpha,'LineStyle','none'); % Set C |
| 212 | + patch(X,Y,colors(4,:),'FaceAlpha',alpha,'LineStyle','none'); % Set D |
| 213 | + |
| 214 | + % Draw ellipse edges separately to ensure they're not covered by others |
| 215 | + patch(X-1,Y+0.5,'w','FaceAlpha',0,'LineStyle','none'); % Set C edge |
| 216 | + patch(X,Y,'w','FaceAlpha',0,'LineStyle','none'); % Set D edge |
205 | 217 | [X,Y] = getEllipse(0.8,1.6,[-1.1 1]);
|
206 |
| - patch(X,Y,'w','FaceAlpha',0,'LineStyle','none'); % ellipse A |
207 |
| - patch(X+1,Y+0.5,'w','FaceAlpha',0,'LineStyle','none'); % ellipse B |
208 |
| - |
209 |
| - text(-3,3,s(1),'HorizontalAlignment','right') |
210 |
| - text(-2,3.5,s(2),'HorizontalAlignment','right') |
211 |
| - text(2,3.5,s(3),'HorizontalAlignment','left') |
212 |
| - text(3,3,s(4),'HorizontalAlignment','left') |
213 |
| - |
214 |
| - text(-2,1.5,A,'HorizontalAlignment','center') |
215 |
| - text(2,1.5,D,'HorizontalAlignment','center') |
216 |
| - text(-1,2.75,B,'HorizontalAlignment','center') |
217 |
| - text(1,2.75,C,'HorizontalAlignment','center') |
218 |
| - |
219 |
| - |
220 |
| - text(-1.4,2.25,AB,'HorizontalAlignment','center') |
221 |
| - text(1.4,2.25,CD,'HorizontalAlignment','center') |
222 |
| - text(0,2.25,BC,'HorizontalAlignment','center') |
223 |
| - text(-1.25,0.5,AC,'HorizontalAlignment','center') |
224 |
| - text(1.25,0.5,BD,'HorizontalAlignment','center') |
225 |
| - text(0,-0.4,AD,'HorizontalAlignment','center') |
226 |
| - |
227 |
| - text(-0.75,1.5,ABC,'HorizontalAlignment','center') |
228 |
| - text(0.75,1.5,BCD,'HorizontalAlignment','center') |
229 |
| - text(-0.4,0.05,ACD,'HorizontalAlignment','center') |
230 |
| - text(0.4,0.05,ABD,'HorizontalAlignment','center') |
231 |
| - |
232 |
| - text(0,0.5,ABCD,'HorizontalAlignment','center') |
233 |
| - |
| 218 | + patch(X,Y,'w','FaceAlpha',0,'LineStyle','none'); % Set A edge |
| 219 | + patch(X+1,Y+0.5,'w','FaceAlpha',0,'LineStyle','none'); % Set B edge |
| 220 | + |
| 221 | + % Set Labels (A, B, C, D) |
| 222 | + text(-3,3,s(1),'HorizontalAlignment','right', 'FontSize', fontSizeSets, 'FontWeight', 'bold'); % Set A |
| 223 | + text(-2,3.5,s(2),'HorizontalAlignment','right', 'FontSize', fontSizeSets, 'FontWeight', 'bold'); % Set B |
| 224 | + text(2,3.5,s(3),'HorizontalAlignment','left', 'FontSize', fontSizeSets, 'FontWeight', 'bold'); % Set C |
| 225 | + text(3,3,s(4),'HorizontalAlignment','left', 'FontSize', fontSizeSets, 'FontWeight', 'bold'); % Set D |
| 226 | + |
| 227 | + % Intersection Labels |
| 228 | + text(-2,1.5,A,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); % A only |
| 229 | + text(2,1.5,D,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); % D only |
| 230 | + text(-1,2.75,B,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); % B only |
| 231 | + text(1,2.75,C,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); % C only |
| 232 | + |
| 233 | + % Pairwise Intersections |
| 234 | + text(-1.4,2.25,AB,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); % A&B |
| 235 | + text(1.4,2.25,CD,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); % C&D |
| 236 | + text(0,2.25,BC,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); % B&C |
| 237 | + text(-1.25,0.5,AC,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); % A&C |
| 238 | + text(1.25,0.5,BD,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); % B&D |
| 239 | + text(0,-0.4,AD,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); % A&D |
| 240 | + |
| 241 | + % Triple Intersections |
| 242 | + text(-0.75,1.5,ABC,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); % A&B&C |
| 243 | + text(0.75,1.5,BCD,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); % B&C&D |
| 244 | + text(-0.4,0.05,ACD,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); % A&C&D |
| 245 | + text(0.4,0.05,ABD,'HorizontalAlignment','center', 'FontSize', fontSizeIntersections, 'FontWeight', 'bold'); % A&B&D |
| 246 | + |
| 247 | + % Quadruple Intersection (A&B&C&D) |
| 248 | + text(0,0.5,ABCD,'HorizontalAlignment','center', 'FontSize', fontSizeSets, 'FontWeight', 'bold'); % A&B&C&D |
| 249 | + |
234 | 250 | otherwise
|
235 | 251 | disp('n must be an integer between 2 and 4.')
|
236 | 252 | end
|
237 | 253 |
|
238 |
| -% Get all text objects |
239 |
| -h=vennfig.findobj('Type','text'); |
240 |
| - |
241 |
| -% Configure texts |
242 |
| -set(h,'fontsize',11,'FontWeight','bold'); |
| 254 | +% Set label color |
| 255 | +h=vennfig.findobj('Type','text'); % Get all text objects |
243 | 256 | for i = 1:length(h)
|
244 |
| - if ismember(h(i).String,sets) |
245 |
| - h(i).FontSize = 14; |
246 |
| - h(i).FontWeight = 'bold'; |
247 |
| - else |
| 257 | + if ~ ismember(h(i).String,sets) |
248 | 258 | h(i).Color = labelC;
|
249 | 259 | end
|
250 | 260 | end
|
|
263 | 273 | end
|
264 | 274 |
|
265 | 275 | %%
|
266 |
| -function [x,y] = getEllipse(r1,r2,C) |
267 |
| -beta = linspace(0,2*pi,100); |
268 |
| -x = r1*cos(beta) - r2*sin(beta); |
269 |
| -y = r1*cos(beta) + r2*sin(beta); |
270 |
| -x = x + C(1,1); |
271 |
| -y = y + C(1,2); |
272 |
| -end |
| 276 | + function [x,y] = getEllipse(r1,r2,C) |
| 277 | + beta = linspace(0,2*pi,100); |
| 278 | + x = r1*cos(beta) - r2*sin(beta); |
| 279 | + y = r1*cos(beta) + r2*sin(beta); |
| 280 | + x = x + C(1,1); |
| 281 | + y = y + C(1,2); |
| 282 | + end |
273 | 283 |
|
274 | 284 | %%
|
275 |
| -function circle(cX,cY,r,faceC,alpha) |
276 |
| -x = cX-r; |
277 |
| -y = cY-r; |
278 |
| -d = 2*r; |
279 |
| -fC = [faceC alpha]; |
280 |
| -rectangle('Position',[x y d d],'Curvature',1,'FaceColor',fC,'LineStyle','none'); |
281 |
| -end |
| 285 | + function circle(cX,cY,r,faceC,alpha) |
| 286 | + x = cX-r; |
| 287 | + y = cY-r; |
| 288 | + d = 2*r; |
| 289 | + h = rectangle('Position', [x y d d], 'Curvature', [1, 1], 'FaceColor', faceC, 'EdgeColor', faceC); |
| 290 | + % Adjust the alpha using the alpha function |
| 291 | + h.FaceAlpha = alpha; |
| 292 | + % Ensure the axes are set to equal scaling for proper aspect ratio |
| 293 | + axis equal; |
| 294 | + end |
282 | 295 |
|
283 | 296 | end
|
0 commit comments