1%%
2%%  wings_util.erl --
3%%
4%%     Various utility functions that not obviously fit somewhere else.
5%%
6%%  Copyright (c) 2001-2011 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%% Note: To keep the call graph clean, wings_util MUST NOT call
14%%       other wings_* modules (except wings_pref).
15
16-module(wings_util).
17-export([share/1,share/3,make_vector/1,
18	 rel2fam/1,
19	 format/2,
20	 key_format/2,
21	 cap/1,upper/1,stringify/1,quote/1,
22	 add_vpos/2,update_vpos/2,
23	 gb_trees_smallest_key/1,gb_trees_largest_key/1,
24	 array_keys/1,array_smallest_key/1,array_greatest_key/1,
25	 array_is_empty/1,array_entries/1,
26	 mapsfind/3,
27	 wxequal/2, wxset_pid/2, min_wx/1,
28	 nice_float/1,nice_vector/1,nice_abs_vector/1,
29         string_to_float/1,
30	 unique_name/2,
31	 is_name_masked/2,
32	 lib_dir/1,
33	 tc/3, profile_start/1, profile_stop/1,
34	 limit/2]).
35
36-define(NEED_OPENGL, 1).
37-define(NEED_ESDL, 1).
38-include("wings.hrl").
39-include_lib("e3d/e3d.hrl").
40
41-import(lists, [foldl/3,reverse/1,member/2,last/1]).
42
43share(X, X, X) -> {X,X,X};
44share(X, X, Z) -> {X,X,Z};
45share(X, Y, Y) -> {X,Y,Y};
46share(X, Y, X) -> {X,Y,X};
47share(X, Y, Z) -> {X,Y,Z}.
48
49share({X,X,X}) -> {X,X,X};
50share({X,X,Z}) -> {X,X,Z};
51share({X,Y,Y}) -> {X,Y,Y};
52share({X,Y,X}) -> {X,Y,X};
53%%
54share({X,X,X,X}) -> {X,X,X,X};
55%%
56share({X,X,X,A}) -> {X,X,X,A};
57share({X,X,Z,X}) -> {X,X,Z,X};
58share({X,Y,X,X}) -> {X,Y,X,X};
59share({X,Y,Y,Y}) -> {X,Y,Y,Y};
60%%
61share({X,X,Y,Y}) -> {X,X,Y,Y};
62share({X,Y,X,Y}) -> {X,Y,X,Y};
63share({X,Y,Y,X}) -> {X,Y,Y,X};
64%%
65share({X,X,Z,A}) -> {X,X,Z,A};
66share({X,Y,X,A}) -> {X,Y,X,A};
67share({X,Y,Z,X}) -> {X,Y,Z,X};
68share({X,Y,Y,A}) -> {X,Y,Y,A};
69share({X,Y,Z,Y}) -> {X,Y,Z,Y};
70share({X,Y,Z,Z}) -> {X,Y,Z,Z};
71%%
72share(Other) -> Other.
73
74make_vector({_,_,_}=Vec) -> Vec;
75make_vector(x) -> {1.0,0.0,0.0};
76make_vector(y) -> {0.0,1.0,0.0};
77make_vector(z) -> {0.0,0.0,1.0};
78make_vector(free) -> free;
79make_vector(normal) -> normal;
80make_vector(intrude) -> normal;
81make_vector({region,normal}) -> normal;
82make_vector(Axis) when Axis == last_axis; Axis == default_axis ->
83    {_,Vec} = wings_pref:get_value(Axis),
84    Vec.
85
86
87key_format(Key, Msg) ->
88    [Key,160,Msg].
89
90%% Like io_lib:format/2, but with very restricted format characters.
91%% BUT allows arguments to ~s to be lists containing Unicode characters.
92%% Now allows ~ts for translation of Asian glyphs.
93%%
94%% Format directives allowed: ~s ~p
95
96format(Format, Args) ->
97    format_1(Format, Args, []).
98
99rel2fam(Rel) ->
100    sofs:to_external(sofs:relation_to_family(sofs:relation(Rel))).
101
102quote(Str) when is_list(Str) ->
103    [$",Str,$"].
104
105stringify({Atom,Other}) when is_atom(Atom) ->
106    translation_string(Atom) ++
107    case stringify(Other) of
108        [] -> [];
109        Str -> "|" ++ Str
110    end;
111stringify(Atom) when is_atom(Atom) ->
112    translation_string(Atom);
113stringify(Int) when is_integer(Int) ->
114    integer_to_list(Int);
115stringify(_Other) -> [].
116
117cap(Str) when is_atom(Str) -> cap(atom_to_list(Str));
118cap(Str) -> cap(Str, true).
119
120cap([Lower|T], true) when $a =< Lower, Lower =< $z ->
121    [Lower-$a+$A|cap(T, false)];
122cap([$_|T], _Any) ->
123    [$\s|cap(T, true)];
124cap([H|T], _Any) ->
125    [H|cap(T, false)];
126cap([], _Flag) -> [].
127
128upper(Str) when is_atom(Str) -> upper(atom_to_list(Str));
129upper([Lower|T]) when $a =< Lower, Lower =< $z ->
130    [Lower-$a+$A|upper(T)];
131upper([H|T]) ->
132    [H|upper(T)];
133upper([]) -> [].
134
135add_vpos(Vs, #we{vp=Vtab}) -> add_vpos(Vs, Vtab);
136add_vpos(Vs, Vtab) ->
137    foldl(fun(V, A) ->
138		  [{V,array:get(V, Vtab)}|A]
139	  end, [], Vs).
140
141update_vpos(Vs, #we{vp=Vtab}) -> update_vpos(Vs, Vtab);
142update_vpos(Vs, Vtab) ->
143    foldl(fun({V,_}, A) ->
144		  [{V,array:get(V, Vtab)}|A];
145	     ({V,_,Dist,Inf}, A) ->
146		  [{V,array:get(V, Vtab),Dist,Inf}|A]
147	  end, [], reverse(Vs)).
148
149gb_trees_smallest_key(Tree) ->
150    {Key,_Val} = gb_trees:smallest(Tree),
151    Key.
152
153gb_trees_largest_key(Tree) ->
154    {Key,_Val} = gb_trees:largest(Tree),
155    Key.
156
157array_keys(Array) ->
158    array:sparse_foldr(fun(I, _, A) -> [I|A] end, [], Array).
159
160array_smallest_key(Array) ->
161    try
162	array:sparse_foldl(fun(I, _, _) -> throw(I) end, [], Array),
163	error(empty_array)
164    catch
165	throw:I when is_integer(I) ->
166	    I
167    end.
168
169array_greatest_key(Array) ->
170    try
171	array:sparse_foldr(fun(I, _, _) -> throw(I) end, [], Array),
172	error(empty_array)
173    catch
174	throw:I when is_integer(I) ->
175	    I
176    end.
177
178array_is_empty(Array) ->
179    try
180	array:sparse_foldr(fun(_, _, _) -> throw(false) end, [], Array),
181	throw(true)
182    catch
183	throw:Empty ->
184	    Empty
185    end.
186
187%% array_entries(Array) -> NumberOfEntries
188%%  Return the number of non-default entries in the array.
189%%
190array_entries(Array) ->
191    array:sparse_foldl(fun(_, _, N) -> N + 1 end, 0, Array).
192
193-spec nice_abs_vector(e3d_vec:vector()) -> iolist().
194
195nice_abs_vector({X,Y,Z}) ->
196    nice_vector({abs(X),abs(Y),abs(Z)}).
197
198-spec nice_vector(e3d_vec:vector()) -> iolist().
199
200nice_vector({X,Y,Z}) ->
201    ["<",
202     wings_util:nice_float(X),"  ",
203     wings_util:nice_float(Y),"  ",
204     wings_util:nice_float(Z),
205     ">"].
206
207nice_float(F) when is_float(F) ->
208    simplify_float(lists:flatten(io_lib:format("~f", [F]))).
209
210simplify_float(F) ->
211    reverse(simplify_float_1(reverse(F))).
212
213simplify_float_1("0."++_=F) -> F;
214simplify_float_1("0"++F) -> simplify_float_1(F);
215simplify_float_1(F) -> F.
216
217
218string_to_float("NaN") -> 0.0;
219string_to_float(Str) ->
220    try list_to_float(Str)
221    catch _:_ -> make_float2(Str)
222    end.
223
224make_float2(Str) ->
225    try float(list_to_integer(Str))
226    catch _:_ -> make_float3(Str)
227    end.
228
229make_float3(Str0) ->
230    Str = lists:flatten(string:tokens(Str0, "\t\s\n")),
231    try list_to_float(Str)
232    catch _:_ -> make_float4(Str) end.
233
234make_float4(Str) ->
235    WithDot = case string:tokens(Str, "eE") of
236		  [Pre,Post] -> Pre ++ ".0e" ++ Post;
237		  [Other] -> Other ++ ".0";
238		  Other -> Other
239	      end,
240    list_to_float(WithDot).
241
242%%
243%% Finds the first map containing Key:=Value
244%%
245-spec mapsfind(term(), term(), list()) -> map()|false.
246mapsfind(Value, Key, [H|T]) ->
247    case H of
248	#{Key:=Value} -> H;
249	_ -> mapsfind(Value, Key, T)
250    end;
251mapsfind(_, _, []) -> false.
252
253
254%% Missing wx funcs
255-record(wx_ref, {ref, type, state}).
256wxequal(#wx_ref{ref=Ref1}, #wx_ref{ref=Ref2}) -> Ref1 =:= Ref2.
257wxset_pid(#wx_ref{}=R, Pid) when is_pid(Pid) ->
258    R#wx_ref{state=Pid}.
259
260min_wx({_,_}=Ver) ->
261    case get(wx_version) of
262        undefined ->
263            case filename:basename(code:lib_dir(wx)) of
264                [$w,$x,$-,Major,_,Minor|_] ->
265                    Version = {Major-$0, Minor-$0},
266                    put(wx_version, Version),
267                    Version >= Ver;
268                _Vsn -> %% erlang src build? assume 19.2 or later
269                    true
270            end;
271        Version ->
272            Version >= Ver
273    end.
274
275%%
276%% Create a unique name by appending digits.
277%%
278
279unique_name(Name, Names) ->
280    case member(Name, Names) of
281	false -> Name;
282	true -> unique_name_1(reverse(Name), Names)
283    end.
284
285unique_name_1([C|Cs], Names) when $0 =< C, C =< $9, Cs /= [] ->
286    unique_name_1(Cs, Names);
287unique_name_1(Name, Names0) ->
288    Base0 = [First|_] = reverse(Name),
289    Names = [N || N <- Names0, hd(N) =:= First],
290    Base = case member($\s, Base0) andalso last(Base0) =/= $\s of
291	       true -> Base0 ++ " ";
292	       false -> Base0
293	   end,
294    unique_name_2(Base, 2, gb_sets:from_list(Names)).
295
296unique_name_2(Base, I, Names) ->
297    Name = Base ++ integer_to_list(I),
298    case gb_sets:is_member(Name, Names) of
299	true -> unique_name_2(Base, I+1, Names);
300	false -> Name
301    end.
302
303tc(Fun,Mod,Line) ->
304    Before = os:timestamp(),
305    R = Fun(),
306    After = os:timestamp(),
307    io:format("~p:~p: Time: ~p\n", [Mod, Line, timer:now_diff(After,Before)]),
308    R.
309
310profile_start(fprof) ->
311    fprof:trace(start),
312    ok;
313profile_start(eprof) ->
314    eprof:start(),
315    profiling = eprof:start_profiling([whereis(wings), self(), whereis(wings_image)]),
316    ok.
317
318profile_stop(fprof) ->
319    fprof:trace(stop),
320    spawn_link(fun() ->
321                       File = "fprof.analysis",
322                       fprof:profile(),
323                       fprof:analyse([{dest, File}, {cols, 120}]),
324                       io:format("Analysis in: ~p~n", [filename:absname(File)]),
325                       eprof:stop()
326               end),
327    ok;
328profile_stop(eprof) ->
329    eprof:stop_profiling(),
330    spawn_link(fun() ->
331                       File = "eprof.analysis",
332                       eprof:log(File),
333                       eprof:analyze(),
334                       io:format("Analysis in: ~p~n", [filename:absname(File)])
335               end).
336
337limit(Val, {'-infinity',infinity}) -> Val;
338limit(Val, {Min,infinity}) when Val < Min -> Min;
339limit(Val, {'-infinity',Max}) when Val > Max -> Max;
340limit(Val, {Min,Max}) when Min < Max, Val < Min -> Min;
341limit(Val, {Min,Max}) when Min < Max, Val > Max -> Max;
342limit(Val, {Min,Max}) when Min < Max -> Val.
343
344%%
345%% Check if name match with the mask.
346%%
347
348is_name_masked(Name,Mask) ->
349    Mask0=string:to_upper(Mask),
350    Name0=string:to_upper(Name),
351    is_name_masked_0(Name0,Mask0,get_mask_pos(Mask0)).
352
353is_name_masked_0(_Name,"*",_MaskPos) -> true;
354is_name_masked_0([],_Mask,_MaskPos) -> false;
355is_name_masked_0(_Name,[],_MaskPos) -> false;
356is_name_masked_0(Name,Name,[]) -> true;
357is_name_masked_0(_Name,_Mask,[]) -> false;
358is_name_masked_0(Name,Mask,[H|[]]=_MaskPos) ->  % *text with only one wildcard
359    Mlen=string:len(Mask),
360    case H of
361    1 ->     % *text value
362        Mask0=string:sub_string(Mask,H+1),
363        MPos=string:rstr(Name,Mask0),
364        (string:len(Name)-MPos+1)=:=(Mlen-1);
365    Mlen ->  % text value*
366        Mask0=string:sub_string(Mask,1,Mlen-1),
367        string:str(Name,Mask0)=:=1;
368    _ ->     % text *value
369        Mask0=string:sub_string(Mask,1,H-1),
370        Mask1=string:sub_string(Mask,H+1),
371        MPos=string:rstr(Name,Mask1),
372        Mlen0=string:len(Mask1),
373        (string:str(Name,Mask0)=:=1) and ((string:len(Name)-MPos)=:=(Mlen0-1))
374    end;
375is_name_masked_0(Name,Mask,[H|[H0|_T0]=_T]=_MaskPos) ->  % *text with more than one wildcard
376    case H of
377    1 ->     % *text value
378        Mask0=string:sub_string(Mask,H+1,H0-1),
379        Mask1=string:sub_string(Mask,H0),
380        case string:str(Name,Mask0) of
381        0 -> false;
382        NPos ->
383            Name0=string:sub_string(Name,NPos+H0-H-1),
384            is_name_masked_0(Name0,Mask1,get_mask_pos(Mask1))
385        end;
386    _ ->     % te*xt value*
387        Mask0=string:sub_string(Mask,1,H-1),
388        Mask1=string:sub_string(Mask,H),
389        case string:str(Name,Mask0) of
390        1 ->
391            Name0=string:sub_string(Name,H),
392            is_name_masked_0(Name0,Mask1,get_mask_pos(Mask1));
393        _ -> false
394        end
395    end.
396
397%%%
398%%% Local functions.
399%%%
400
401format_1("~s"++F, [S0|Args], Acc) ->
402    S = if
403	    is_atom(S0) -> atom_to_list(S0);
404	    is_list(S0) -> S0;
405	    true ->
406		io:format("Bad string formatter for ~p in ~p~n",
407			  [S0, lists:flatten(reverse(Acc) ++ F)]),
408		error(badarg)
409	end,
410    format_1(F, Args, [S|Acc]);
411format_1("~p"++F, [S0|Args], Acc) ->
412    S = format_p(S0),
413    format_1(F, Args, [S|Acc]);
414format_1("~~"++F, Args, Acc) ->
415    format_1(F, Args, [$~|Acc]);
416format_1("~ts"++F, Args, Acc) ->
417    format_1("~s"++F, Args, Acc);
418format_1([C|F], Args, Acc) when C =/= $~ ->
419    format_1(F, Args, [C|Acc]);
420format_1([], [], Acc) -> reverse(Acc).
421
422format_p(Str) when is_list(Str)  ->
423    [$",Str,$"];
424format_p(Str) ->
425    io_lib:format("~p", [Str]).
426
427lib_dir(wings) ->
428    case code:lib_dir(wings) of
429	{error,bad_name} ->
430	    ["wings.beam","ebin"|Rev] = lists:reverse(filename:split(code:which(wings))),
431	    filename:join(lists:reverse(Rev));
432	Dir ->
433	    Dir
434    end;
435lib_dir(Lib) ->
436    code:lib_dir(Lib).
437
438get_mask_pos([]) -> [];
439get_mask_pos(Mask) ->
440    get_mask_pos_1(Mask,0,[]).
441
442get_mask_pos_1([],_,Acc) -> Acc;
443get_mask_pos_1(Mask,Offset,Acc) ->
444    case string:str(Mask,"*") of
445    0 -> Acc;
446    Pos ->
447        Pos0=Pos+Offset,
448        get_mask_pos_1(string:sub_string(Mask,Pos+1),Pos0,Acc++[Pos0])
449    end.
450
451
452% % % % % % % % % % % % % % % % % % %
453%% Strings for Translating Hotkeys %%
454% % % % % % (not elegant) % % % % % %
455
456%%%% Selection Modes
457translation_string(vertex) -> ?__(1,"Vertex");
458translation_string(edge)   -> ?__(2,"Edge");
459translation_string(face)   -> ?__(3,"Face");
460translation_string(body)   -> ?__(4,"Body");
461translation_string(light)  -> ?__(5,"Light");
462
463%%%% Menu Headers
464translation_string(file)   -> ?__(6,"File");
465translation_string(edit)   -> ?__(7,"Edit");
466translation_string(view)   -> ?__(8,"View");
467translation_string(select) -> ?__(9,"Select");
468translation_string(tools)  -> ?__(10,"Tools");
469translation_string(window) -> ?__(11,"Window");
470translation_string(help)   -> ?__(12,"Help");
471
472%%%% File Menu
473translation_string(new)             -> ?__(13,"New");
474translation_string(open)            -> ?__(14,"Open");
475translation_string(merge)           -> ?__(15,"Merge");
476translation_string(save)            -> ?__(16,"Save");
477translation_string(save_as)         -> ?__(17,"Save As");
478translation_string(save_selected)   -> ?__(18,"Save Selected");
479translation_string(save_incr)       -> ?__(19,"Save Incrementally");
480translation_string(revert)          -> ?__(20,"Revert");
481translation_string(import)          -> ?__(21,"Import");
482translation_string(export)          -> ?__(22,"Export");
483translation_string(export_selected) -> ?__(23,"Export Selected");
484translation_string(import_image)    -> ?__(24,"Import Image");
485translation_string(render)          -> ?__(25,"Render");
486translation_string(install_plugin)  -> ?__(26,"Install Plugin");
487translation_string(quit)            -> ?__(27,"Exit");
488
489%%%% Import Export Formats
490%% these probably don't need translating
491
492%%%% Edit Menu
493translation_string(undo_toggle)     -> ?__(28,"Undo/Redo");
494translation_string(undo)            -> ?__(29,"Undo");
495translation_string(redo)            -> ?__(30,"Redo");
496translation_string(repeat)          -> ?__(31,"Repeat");
497translation_string(repeat_args)     -> ?__(32,"Repeat Args");
498translation_string(repeat_drag)     -> ?__(33,"Repeat Drag");
499translation_string(purge_undo)      -> ?__(34,"Purge Undo");
500translation_string(preferences)     -> ?__(35,"Preferences");
501translation_string(prefs)           -> ?__(36,"Prefs");
502translation_string(plugin_manager)  -> ?__(37,"Plugin Manager");
503
504%%%% View Menu
505translation_string(show_groundplane)    -> ?__(38,"Show Ground Plane");
506translation_string(show_axes)           -> ?__(39,"Show Axes");
507translation_string(show_info_text)      -> ?__(40,"Show Info Text");
508translation_string(workmode)            -> ?__(41,"Workmode");
509translation_string(wireframe)           -> ?__(42,"Wireframe");
510translation_string(shade)               -> ?__(43,"Shade");
511translation_string(toggle_wirefrfame)   -> ?__(44,"Toggle Wireframe");
512translation_string(show_edges)          -> ?__(45,"Show Edges");
513translation_string(show_wire_backfaces) -> ?__(46,"Show Wireframe Backfaces");
514translation_string(smooth_proxy)        -> ?__(47,"Toggle Smooth Proxy");
515translation_string(quick_preview)       -> ?__(48,"Quick Smoothed Preview");
516translation_string(show_bb)             -> ?__(49,"Show Bounding Box");
517translation_string(clip_plane)          -> ?__(50,"Clipping Plane");
518translation_string(show_normals)        -> ?__(51,"Show Normals");
519translation_string(show_colors)         -> ?__(52,"Show Colors");
520translation_string(show_materials)      -> ?__(53,"Show Materials");
521translation_string(show_textures)       -> ?__(54,"Show Textures");
522translation_string(scene_lights)        -> ?__(55,"Scene Lights");
523translation_string(toggle_lights)       -> ?__(56,"Toggle Lights");
524translation_string(orthogonal_view)     -> ?__(57,"Orthographic View");
525translation_string(reset)               -> ?__(58,"Reset View");
526translation_string(aim)                 -> ?__(59,"Aim");
527translation_string(highlight_aim)       -> ?__(60,"Highlight Aim");
528translation_string(frame)               -> ?__(61,"Frame");
529translation_string(frame_mode)          -> ?__(62,"Frame Mode");
530translation_string(align_to_selection)  -> ?__(63,"Align to Selection");
531translation_string(next)                -> ?__(64,"Next");
532translation_string(pref)                -> ?__(65,"Prev");
533translation_string(current)             -> ?__(66,"Current");
534translation_string(jump)                -> ?__(67,"Jump");
535translation_string(along)               -> ?__(68,"Along");
536translation_string(rename)              -> ?__(69,"Rename");
537translation_string(delete_all)          -> ?__(70,"Delete All");
538translation_string(camera_settings)     -> ?__(71,"Camera Settings");
539translation_string(auto_rotate)         -> ?__(72,"Auto Rotate");
540
541%%%% Select Menu
542translation_string(deselect)            -> ?__(73,"Deselect");
543translation_string(more)                -> ?__(74,"More");
544translation_string(less)                -> ?__(75,"Less");
545translation_string(similar)             -> ?__(76,"Similar");
546translation_string(edge_loop)           -> ?__(77,"Edge Loop");
547translation_string(edge_loop_to_region) -> ?__(78,"Edge Loop to Region");
548translation_string(edge_ring)           -> ?__(79,"Edge Ring");
549translation_string(prev_edge_loop)      -> ?__(80,"Previous Edge Loop");
550translation_string(next_edge_loop)      -> ?__(81,"Next Edge Loop");
551translation_string(edge_link_incr)      -> ?__(82,"Edge Link Increase");
552translation_string(edge_link_decr)      -> ?__(83,"Edge Link Decrease");
553translation_string(edge_ring_incr)      -> ?__(84,"Edge Ring Increase");
554translation_string(edge_ring_decr)      -> ?__(85,"Edge Ring Decrease");
555translation_string(adjacent)            -> ?__(86,"Adjacent");
556translation_string(inverse)             -> ?__(87,"Inverse");
557translation_string(hide_selected)       -> ?__(88,"Hide Selected");
558translation_string(hide_unselected)     -> ?__(89,"Hide Unselected");
559translation_string(lock_unselected)     -> ?__(90,"Lock Unselected");
560translation_string(show_all)            -> ?__(91,"Show All");
561translation_string(store_selection)     -> ?__(92,"Store Selection");
562translation_string(recall_selection)    -> ?__(93,"Recall Selection");
563translation_string(new_group)           -> ?__(94,"New Group");
564translation_string(oriented_faces)      -> ?__(95,"Similar Normals");
565translation_string(similar_area)        -> ?__(96,"Similar Area");
566translation_string(delete_group)        -> ?__(97,"Delete Group");
567translation_string(add_to_group)        -> ?__(98,"Add to Group");
568translation_string(subtract_from_group) -> ?__(99,"Subtract from Group");
569translation_string(select_group)        -> ?__(100,"Select Group");
570translation_string(union_group)         -> ?__(101,"Union Group");
571translation_string(subtract_group)      -> ?__(102,"Subtract Group");
572translation_string(intersect_group)     -> ?__(103,"Intersect Group");
573
574%%%% Select By Submenu
575translation_string(by)                      -> ?__(104,"By");
576translation_string(hard_edges)              -> ?__(105,"Hard Edges");
577translation_string(isolated_vertices)       -> ?__(106,"Isolated Vertices");
578translation_string(nonplanar_faces)         -> ?__(107,"Non-Planar Faces");
579translation_string(vertices_with)           -> ?__(108,"Vertices With");
580translation_string(faces_with)              -> ?__(109,"Faces With");
581translation_string(non_quad)                -> ?__(110,"Non Quadrangle");
582translation_string(odd)                     -> ?__(111,"Odd");
583translation_string(even)                    -> ?__(112,"Even");
584translation_string(random)                  -> ?__(113,"Random");
585translation_string(short_edges)             -> ?__(114,"Short Edges");
586translation_string(sharp_edges)             -> ?__(115,"Sharp Edges");
587translation_string(vertex_path)             -> ?__(116,"Vertex Path");
588translation_string(fewest_edges_path)       -> ?__(117,"Fewest Edges Path");
589translation_string(dijkstra_shortest_path)  -> ?__(118,"Shortest Path (Dijkstra)");
590translation_string(astar_shortest_path)     -> ?__(119,"Shortest Path (A-Star)");
591translation_string(material_edges)          -> ?__(120,"Material Edges");
592
593
594
595%%%% Tools Menu
596translation_string(align)            -> ?__(121,"Align");
597translation_string(save_bb)          -> ?__(122,"Save Bounding Box");
598translation_string(scale_to_bb)      -> ?__(123,"Scale to Bounding Box");
599translation_string(scale_to_bb_prop) -> ?__(124,"Scale to Bounding Box Proportionally");
600translation_string(move_to_bb)       -> ?__(125,"Move to Bounding Box");
601translation_string(virtual_mirror)   -> ?__(126,"Virtual Mirror");
602translation_string(create)           -> ?__(127,"Create");
603translation_string(break)            -> ?__(128,"Break");
604translation_string(freeze)           -> ?__(129,"Freeze");
605translation_string(screenshot)       -> ?__(130,"Screenshot");
606translation_string(area_volume_info) -> ?__(131,"Area Volume Info");
607translation_string(scene_size_info)  -> ?__(132,"Memory Usage");
608translation_string(put_on_ground)    -> ?__(133,"Put on Ground");
609translation_string(unitize)          -> ?__(134,"Unitize");
610translation_string(tweak)            -> ?__(135,"Tweak");
611translation_string(snap_image_mode)  -> ?__(136,"Snap Image");
612
613%%%% Window Menu
614translation_string(outliner)         -> ?__(137,"Outliner");
615translation_string(object)           -> ?__(138,"Geometry Graph");
616translation_string(palette)          -> ?__(139,"Palette");
617translation_string(console)          -> ?__(140,"Console");
618translation_string(geom_viewer)      -> ?__(141,"New Geomerty Window");
619translation_string(uv_editor_window) -> ?__(142,"UV Editor Window");
620
621%%%% Help Menu
622translation_string(getting_started)      -> ?__(143,"Getting Started");
623translation_string(one_or_two)           -> ?__(144,"Mouse Buttons");
624translation_string(international)        -> ?__(145,"International Keyboards");
625translation_string(hotkeys)              -> ?__(146,"Defined Hotkeys");
626translation_string(defining_hotkeys)     -> ?__(147,"Defining Hotkeys");
627%%translation_string(advanced_menus)       -> ?__(148,"Advanced Menus");
628translation_string(default_commands)     -> ?__(149,"Default Commands");
629translation_string(performance_tips)     -> ?__(150,"Performance Tips");
630translation_string(opengl_info)          -> ?__(151,"OpenGL Info");
631translation_string(about)                -> ?__(152,"About");
632
633%%%% Primitives Menu
634translation_string(shapes)            -> ?__(153,"Shapes");
635translation_string(tetrahedron)       -> ?__(154,"Tetrahedron");
636translation_string(octahedron)        -> ?__(155,"Octahedron");
637translation_string(octotoad)          -> ?__(156,"Octotoad");
638translation_string(dodecahedron)      -> ?__(157,"Dodecahedron");
639translation_string(icosahedron)       -> ?__(158,"Icosahedron");
640translation_string(cube)              -> ?__(159,"Cube");
641translation_string(cylinder)          -> ?__(160,"Cylinder");
642translation_string(cone)              -> ?__(161,"Cone");
643translation_string(sphere)            -> ?__(162,"Sphere");
644translation_string(torus)             -> ?__(163,"Torus");
645translation_string(grid)              -> ?__(164,"Grid");
646translation_string(material)          -> ?__(165,"Material");
647translation_string(image)             -> ?__(166,"Image");
648translation_string(image_plane)       -> ?__(167,"Image Plane");
649translation_string(text)              -> ?__(168,"Text");
650%%%% More
651translation_string(gear)              -> ?__(169,"Gear");
652translation_string(tube)              -> ?__(170,"Tube");
653translation_string(geodome)           -> ?__(171,"Geo Dome");
654translation_string(knot)              -> ?__(172,"Torus Knot");
655translation_string(ncube)             -> ?__(173,"N-Cube");
656translation_string(ngon)              -> ?__(174,"N-Gon");
657translation_string(regularplane)      -> ?__(175,"Regular Plane");
658translation_string(lumpyplane)        -> ?__(176,"Lumpy Plane");
659translation_string(wavyplane)         -> ?__(177,"Wavy Plane");
660translation_string(sombreroplane)     -> ?__(178,"Sombrero Plane");
661translation_string(spiral)            -> ?__(179,"Spiral");
662translation_string(spring)            -> ?__(180,"Spring");
663translation_string(uvtorus)           -> ?__(181,"UV Torus");
664translation_string(lutorus)           -> ?__(182,"Lumpy Torus");
665translation_string(sptorus)           -> ?__(183,"Spiral Torus");
666
667%%%% Common Axis Descriptors
668translation_string(normal)       -> ?__(184,"Normal");
669translation_string(free)         -> ?__(185,"Free");
670translation_string(center)       -> ?__(186,"Center");
671translation_string(central_axis) -> ?__(187,"Central Axis");
672translation_string(x)            -> ?__(188,"X");
673translation_string(y)            -> ?__(189,"Y");
674translation_string(z)            -> ?__(190,"Z");
675translation_string(neg_x)        -> ?__(191,"-X");
676translation_string(neg_y)        -> ?__(192,"-Y");
677translation_string(neg_z)        -> ?__(193,"-Z");
678translation_string(last_axis)    -> ?__(194,"Last Axis");
679translation_string(default_axis) -> ?__(195,"Default Axis");
680translation_string(true)         -> ?__(196,"True");
681translation_string(false)        -> ?__(197,"False");
682translation_string(all)          -> ?__(198,"All");
683translation_string(pick_all)     -> ?__(199,"Pick All");
684translation_string(radial_x)     -> ?__(200,"Radial X");
685translation_string(radial_y)     -> ?__(201,"Radial Y");
686translation_string(radial_z)     -> ?__(202,"Radial Z");
687translation_string('ASK')        -> ?__(203,"ASK");
688translation_string(lmb)          -> ?__(204,"LMB");
689translation_string(mmb)          -> ?__(205,"MMB");
690translation_string(rmb)          -> ?__(206,"RMB");
691translation_string(relative)     -> ?__(207,"Relative");
692translation_string(absolute)     -> ?__(208,"Absolute");
693
694%%%% Common Commands
695translation_string(move)           -> ?__(209,"Move");
696translation_string(rotate)         -> ?__(210,"Rotate");
697translation_string(scale)          -> ?__(211,"Scale");
698translation_string(uniform)        -> ?__(212,"Uniform");
699translation_string(extrude)        -> ?__(213,"Extrude");
700translation_string(extract)        -> ?__(214,"Extract");
701translation_string(dissolve)       -> ?__(215,"Dissolve");
702translation_string(collapse)       -> ?__(216,"Collapse");
703translation_string(delete)         -> ?__(217,"Delete");
704translation_string(flatten)        -> ?__(218,"Flatten");
705translation_string(smooth)         -> ?__(219,"Smooth");
706translation_string(tighten)        -> ?__(220,"Tighten");
707translation_string(bevel)          -> ?__(221,"Bevel");
708translation_string(weld)           -> ?__(222,"Weld");
709translation_string(vertex_color)   -> ?__(223,"Vertex Color");
710translation_string(move_planar)    -> ?__(290,"Move Planar");
711
712%%%% Absolute Commands
713translation_string(snap)           -> ?__(224,"Snap");
714translation_string(nsnap)          -> ?__(225,"Snap|Numeric Entry");
715translation_string(ctscale)        -> ?__(226,"Scale to Target Size");
716translation_string(cscale)         -> ?__(227,"Scale|Center");
717
718%%%% Intersect
719translation_string(intersect)      -> ?__(228,"Intersect");
720translation_string(stay_on_line)   -> ?__(229,"Stay on Line");
721translation_string(stay_on_plane)  -> ?__(230,"Stay on Plane");
722translation_string(arc_intersect)  -> ?__(289,"Rotate to Target");
723
724%%%% Vertex Menu
725translation_string(connecting_edges) -> ?__(231,"Connecting Edges");
726translation_string(bend)             -> ?__(232,"Bend");
727translation_string(shift)            -> ?__(233,"Shift");
728
729%%%% Vertex|Deform
730translation_string(deform)          -> ?__(234,"Deform");
731translation_string(crumple)         -> ?__(235,"Crumple");
732translation_string(taper)           -> ?__(236,"Taper");
733translation_string(twist)           -> ?__(237,"Twist");
734translation_string(torque)          -> ?__(238,"Torque");
735translation_string(inflate)         -> ?__(239,"Inflate");
736translation_string(cylindrilize)    -> ?__(240,"Inflate Cylindrically");
737translation_string(shear)           -> ?__(241,"Shear");
738
739%%%% Edge Menu
740translation_string(slide)              -> ?__(242,"Slide");
741translation_string(clean_dissolve)     -> ?__(243,"Clean Dissolve");
742translation_string(cut)                -> ?__(244,"Cut");
743translation_string(connect)            -> ?__(245,"Connect");
744translation_string(hardness)           -> ?__(246,"Hardness");
745translation_string(soft)               -> ?__(247,"Soft");
746translation_string(hard)               -> ?__(248,"Hard");
747translation_string(circularise)        -> ?__(249,"Circularise");
748translation_string(circularise_center) -> ?__(250,"Circularise|Center");
749translation_string(loop_cut)           -> ?__(251,"Loop Cut");
750translation_string(turn_ccw)           -> ?__(252,"Turn CCW");
751translation_string(turn_cw)            -> ?__(253,"Turn CW");
752translation_string(turn_optimized)     -> ?__(254,"Turn Optimized");
753
754%%%% Face Menu
755translation_string(bridge)             -> ?__(255,"Bridge");
756translation_string(bump)               -> ?__(256,"Bump");
757translation_string(extrude_region)     -> ?__(257,"Extrude Region");
758translation_string(extract_region)     -> ?__(258,"Extract Region");
759translation_string(sweep_extrude)      -> ?__(259,"Sweep Extrude");
760translation_string(sweep_region)       -> ?__(260,"Sweep Region");
761translation_string(sweep_extract)      -> ?__(261,"Sweep Extract");
762translation_string(inset)              -> ?__(262,"Inset");
763translation_string(intrude)            -> ?__(263,"Intrude");
764translation_string(lift)               -> ?__(264,"Lift");
765translation_string(put_on)             -> ?__(265,"Put On");
766translation_string(clone_on)           -> ?__(266,"Clone On");
767translation_string(mirror)             -> ?__(267,"Mirror");
768translation_string(mirror_separate)    -> ?__(268,"Mirror Separate");
769translation_string(tesselate)          -> ?__(269,"Tesselate");
770translation_string(trianglulate)       -> ?__(270,"Triangulate");
771translation_string(quadrangulate)      -> ?__(271,"Quadrangulate");
772translation_string(untriangulate)      -> ?__(272,"Untriangulate");
773translation_string(hide)               -> ?__(273,"Hide");
774translation_string(wpc_autouv)         -> ?__(274,"AutoUV");
775translation_string(segment)            -> ?__(275,"Segment");
776translation_string(segment_old)        -> ?__(276,"Segment Old");
777translation_string(force_seg)          -> ?__(277,"Force Segment");
778
779%%%% Object Menu
780translation_string(flip)                 -> ?__(278,"Flip");
781translation_string(invert)               -> ?__(279,"Invert");
782translation_string(doo_sabin)            -> ?__(280,"Doo Sabin");
783translation_string(combine)              -> ?__(281,"Combine");
784translation_string(separate)             -> ?__(282,"Separate");
785translation_string(cleanup)              -> ?__(283,"Cleanup");
786translation_string(auto_smooth)          -> ?__(284,"Auto Smooth");
787translation_string(duplicate)            -> ?__(285,"Duplicate");
788translation_string(vertex_color_mode)    -> ?__(286,"Vertex Color Mode");
789translation_string(to_arealight)         -> ?__(287,"To Area Light");
790translation_string(materials_to_colors)  -> ?__(288,"Materials to Colors");
791
792%%%%
793%%%%   Translation strings used so far 1 - 290
794%%%%
795
796%%%% Others as yet to be added are processed here
797translation_string(Atom) when is_atom(Atom) ->
798    wings_util:cap(atom_to_list(Atom)).
799