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