1%% 2%% wings_info.erl -- 3%% 4%% Format information about the selected items. 5%% 6%% Copyright (c) 2016 Bjorn Gustavsson 7%% 8%% See the file "license.terms" for information on usage and redistribution 9%% of this file, and for a DISCLAIMER OF ALL WARRANTIES. 10%% 11%% $Id$ 12%% 13 14-module(wings_info). 15-export([info/1]). 16 17-include("wings.hrl"). 18-import(lists, [foldl/3,sort/1]). 19 20-spec info(#st{}) -> iolist(). 21 22info(#st{sel=[]}) -> 23 []; 24info(St) -> 25 case wings_wm:get_prop(show_info_text) of 26 false -> 27 []; 28 true -> 29 [basic(St),measure(St)] 30 end. 31 32basic(#st{selmode=body,sel=[_]}=St) -> 33 deep_object_info(St); 34basic(#st{selmode=body}=St) -> 35 summary_object_info(St); 36basic(#st{selmode=Mode,sel=[_]}=St) -> 37 MF = fun(Items, _) -> 38 case gb_sets:size(Items) of 39 Sz when Sz < 5 -> 40 {items,gb_sets:to_list(Items)}; 41 Sz -> 42 {size,Sz} 43 end 44 end, 45 RF = fun(T, ignore) -> T end, 46 case wings_sel:dfold(MF, RF, ignore, St) of 47 {items,[_]=Vs} -> 48 io_lib:format(str(Mode, one), Vs); 49 {items,Vs} -> 50 io_lib:format(str(Mode, few), [item_list(Vs)]); 51 {size,N} -> 52 io_lib:format(str(Mode, many), [N]) 53 end; 54basic(#st{selmode=Mode,sel=Sel}=St) -> 55 On = length(Sel), 56 MF = fun(Items, _) -> gb_sets:size(Items) end, 57 RF = fun erlang:'+'/2, 58 N = wings_sel:dfold(MF, RF, 0, St), 59 io_lib:format(str(Mode, multiple_objects), [N,On]). 60 61measure(#st{sel=[_,_,_|_]}) -> 62 %% Nothing to do when more than two objects selected. 63 []; 64measure(#st{selmode=vertex}=St) -> 65 MF = fun(Vs0, We) -> 66 case gb_sets:size(Vs0) =< 2 of 67 true -> 68 Vs = gb_sets:to_list(Vs0), 69 [wings_vertex:pos(V, We) || V <- Vs]; 70 false -> 71 [none,none,none] 72 end 73 end, 74 RF = fun erlang:'++'/2, 75 Positions = wings_sel:dfold(MF, RF, [], St), 76 measure_vs(Positions); 77measure(#st{selmode=edge}=St) -> 78 MF = fun(Es, #we{id=Id,es=Etab}=We) -> 79 EF = fun(Edge) -> 80 #edge{vs=Va,ve=Vb} = array:get(Edge, Etab), 81 {{Id,Edge}, 82 wings_vertex:pos(Va, We), 83 wings_vertex:pos(Vb, We)} 84 end, 85 case gb_sets:size(Es) =< 2 of 86 true -> 87 [EF(E) || E <- gb_sets:to_list(Es)]; 88 false -> 89 [none,none,none] 90 end 91 end, 92 RF = fun erlang:'++'/2, 93 EdgeInfo = wings_sel:dfold(MF, RF, [], St), 94 [measure_es(EdgeInfo)|enhanced_info(edge, EdgeInfo)]; 95measure(#st{selmode=face}=St) -> 96 MF = fun(Fs, #we{id=Id}=We) -> 97 FF = fun(Face) -> 98 Center = wings_face:center(Face, We), 99 N = wings_face:normal(Face, We), 100 Mat = wings_facemat:face(Face, We), 101 Area = area_info(Face, We), 102 {{Id,Face},Center,N,Mat,Area} 103 end, 104 case gb_sets:size(Fs) =< 2 of 105 true -> 106 [FF(Face) || Face <- gb_sets:to_list(Fs)]; 107 false -> 108 [none,none,none] 109 end 110 end, 111 RF = fun erlang:'++'/2, 112 FaceInfo = wings_sel:dfold(MF, RF, [], St), 113 [measure_fs(FaceInfo)|enhanced_info(face, FaceInfo)]; 114measure(_) -> []. 115 116measure_vs([Pos]) -> 117 io_lib:format(?__(1,". Position ~s"), [wings_util:nice_vector(Pos)]); 118measure_vs([PosA,PosB]) -> 119 PosDiff = e3d_vec:sub(PosB, PosA), 120 Dist = e3d_vec:len(PosDiff), 121 io_lib:format(?__(2,". Distance ~s ~s"), 122 [wings_util:nice_float(Dist), 123 wings_util:nice_vector(PosDiff)]); 124measure_vs(_) -> []. 125 126measure_es([{_,PosA,PosB}]) -> 127 PosDiff = e3d_vec:sub(PosB, PosA), 128 Length = e3d_vec:len(PosDiff), 129 Mid = e3d_vec:average(PosA, PosB), 130 io_lib:format(". " ++ ?__(1,"Midpoint ~s>\nLength ~s") ++ 131 " ~s", 132 [wings_util:nice_vector(Mid), 133 wings_util:nice_float(Length), 134 wings_util:nice_vector(PosDiff)]); 135measure_es([{_E0,Pos0A,Pos0B},{_E1,Pos1A,Pos1B}]) -> 136 V0 = e3d_vec:sub(Pos0B, Pos0A), 137 V1 = e3d_vec:sub(Pos1B, Pos1A), 138 RawAngle = e3d_vec:degrees(V0, V1), 139 Angle = case {Pos0A,Pos0B} of 140 {Pos1B,_} -> 180.0 - RawAngle; 141 {_,Pos1A} -> 180.0 - RawAngle; 142 {_,_} -> RawAngle 143 end, 144 io_lib:format(?__(2,". Angle ~s") ++ "~c", 145 [wings_util:nice_float(Angle),?DEGREE]); 146measure_es(_) -> []. 147 148measure_fs([{_,Center,_,Mat,Area}]) -> 149 io_lib:format(?__(1,". Midpoint ~s\n" 150 "Material ~ts.") ++ Area, 151 [wings_util:nice_vector(Center), 152 Mat]); 153measure_fs([{_,_,N0,_,_},{_,_,N1,_,_}]) -> 154 Angle = e3d_vec:degrees(N0, N1), 155 io_lib:format(?__(2,". Angle ~s") ++ "~c", 156 [wings_util:nice_float(Angle),?DEGREE]); 157measure_fs(_) -> []. 158 159enhanced_info(Mode, Info) -> 160 case wings_pref:get_value(info_enhanced_text) of 161 true -> 162 enhanced_info_1(Mode, Info); 163 false -> 164 [] 165 end. 166 167enhanced_info_1(edge, [{{Id0,E0},Pos0A,Pos0B}, 168 {{Id1,E1},Pos1A,Pos1B}]) -> 169 Length0 = e3d_vec:dist(Pos0A, Pos0B), 170 Length1 = e3d_vec:dist(Pos1A, Pos1B), 171 Mid0 = e3d_vec:average(Pos0A, Pos0B), 172 Mid1 = e3d_vec:average(Pos1A, Pos1B), 173 MidDiff = e3d_vec:sub(Mid1, Mid0), 174 Dist = e3d_vec:len(MidDiff), 175 Diff = abs(Length0 - Length1), 176 io_lib:format(?__(42,"\nDistance ~s")++" ~s\n"++ 177 ?__(43,"Object~s")++" "++?__(41,"Edge~s ~s")++" "++ 178 ?__(43,"Object~s")++" "++?__(41,"Edge~s ~s")++" "++ 179 ?__(45,"Difference ~s"), 180 [wings_util:nice_float(Dist), 181 wings_util:nice_abs_vector(MidDiff), 182 wings_util:stringify(Id0), 183 wings_util:stringify(E0), 184 wings_util:nice_float(Length0), 185 wings_util:stringify(Id1), 186 wings_util:stringify(E1), 187 wings_util:nice_float(Length1), 188 wings_util:nice_float(Diff)]); 189enhanced_info_1(face, [{{Id0,F0},Center0,_,_,Area0}, 190 {{Id1,F1},Center1,_,_,Area1}]) -> 191 CenterDiff = e3d_vec:sub(Center1, Center0), 192 Dist = e3d_vec:len(CenterDiff), 193 io_lib:format(?__(42,"\nDistance ~s") ++ " ~s\n" ++ 194 ?__(43,"Object~s")++" "++?__(48,"Face~s")++Area0++" "++ 195 ?__(43,"Object~s")++" "++?__(48,"Face~s")++Area1, 196 [wings_util:nice_float(Dist), 197 wings_util:nice_abs_vector(CenterDiff), 198 wings_util:stringify(Id0), 199 wings_util:stringify(F0), 200 wings_util:stringify(Id1), 201 wings_util:stringify(F1)]); 202enhanced_info_1(_, _) -> []. 203 204item_list(Items) -> 205 item_list(Items, ""). 206 207item_list([Item|Items], Sep) -> 208 [Sep,integer_to_list(Item)|item_list(Items, ", ")]; 209item_list([], _Sep) -> []. 210 211deep_object_info(St) -> 212 MF = fun(_, We) -> deep_object_info_1(We) end, 213 RF = fun(S, ignore) -> S end, 214 wings_sel:dfold(MF, RF, ignore, St). 215 216deep_object_info_1(We) when ?IS_LIGHT(We) -> 217 wings_light:info(We); 218deep_object_info_1(#we{id=Id,name=Name,fs=Ftab,es=Etab,vp=Vtab}=We) -> 219 Faces = gb_trees:size(Ftab), 220 case array:size(Etab) < 50000 of 221 true -> 222 Edges = wings_util:array_entries(Etab), 223 Vertices = wings_util:array_entries(Vtab), 224 Format = ?__(new_object_info, 225 "Object ~p \"~ts\" has ~p polygons, " 226 "~p edges, ~p vertices~s~s."); 227 false -> 228 Edges = array:size(Etab), 229 Vertices = array:size(Vtab), 230 Format = ?__(new_object_info2, 231 "Object ~p \"~ts\" has ~p polygons, " 232 "~~~p edges, ~~~p vertices~s~s.") 233 end, 234 io_lib:format(Format, [Id,Name,Faces,Edges,Vertices, 235 vtx_attributes(We),hole_info(We)]). 236 237vtx_attributes(We) -> 238 case wings_va:any_attributes(We) of 239 false -> ""; 240 true -> ", " ++ ?__(1,"vertex attributes") 241 end. 242 243hole_info(#we{holes=[]}) -> 244 ""; 245hole_info(#we{holes=Holes}) -> 246 case length(Holes) of 247 1 -> [", 1 ",?__(1,"hole")]; 248 N -> [", ",integer_to_list(N)," ",?__(2,"holes")] 249 end. 250 251summary_object_info(St) -> 252 MF = fun(_, We) -> 253 #we{fs=Ftab,es=Etab,vp=Vtab} = We, 254 Faces = gb_trees:size(Ftab), 255 Appr = array:size(Etab) >= 50000, 256 case Appr of 257 false -> 258 Edges = wings_util:array_entries(Etab), 259 Vertices = wings_util:array_entries(Vtab); 260 true -> 261 Edges = array:size(Etab), 262 Vertices = array:size(Vtab) 263 end, 264 {1,Faces,Edges,Vertices} 265 end, 266 RF = fun({A,B,C,D}, {A0,B0,C0,D0}) -> 267 {A+A0,B+B0,C+C0,D+D0} 268 end, 269 Acc0 = {0,0,0,0}, 270 {N,Faces,Edges,Vertices} = wings_sel:dfold(MF, RF, Acc0, St), 271 io_lib:format(?__(2, 272 "~p objects, ~p faces, ~p edges, ~p vertices"), 273 [N,Faces,Edges,Vertices]). 274 275area_info(Face, We) -> 276 case wings_face:vertices(Face, We) =< 50 of 277 true -> 278 A = wings_face:area(Face, We), 279 io_lib:format(?__(40," Area ~s"), [wings_util:nice_float(A)]); 280 false -> 281 [] 282 end. 283 284str(vertex, one) -> 285 ?__(1,"Vertex ~p selected"); 286str(vertex, few) -> 287 ?__(2,"Vertices ~s selected"); 288str(vertex, many) -> 289 ?__(3,"~p vertices selected"); 290str(vertex, multiple_objects) -> 291 ?__(4,"~p vertices selected in ~p objects"); 292str(edge, one) -> 293 ?__(5,"Edge ~p selected"); 294str(edge, few) -> 295 ?__(6,"Edges ~s selected"); 296str(edge, many) -> 297 ?__(7,"~p edges selected"); 298str(edge, multiple_objects) -> 299 ?__(8,"~p edges selected in ~p objects"); 300str(face, one) -> 301 ?__(9,"Face ~p selected"); 302str(face, few) -> 303 ?__(10,"Faces ~s selected"); 304str(face, many) -> 305 ?__(11,"~p faces selected"); 306str(face, multiple_objects) -> 307 ?__(12,"~p faces selected in ~p objects"). 308