%%
%%  wings_vec.erl --
%%
%%     This module implements "vectors" and the secondary selection mode.
%%
%%  Copyright (c) 2002-2003 Bjorn Gustavsson.
%%
%%  See the file "license.terms" for information on usage and redistribution
%%  of this file, and for a DISCLAIMER OF ALL WARRANTIES.
%%
%%     $Id: wings_vec.erl,v 1.102 2003/11/10 20:14:25 bjorng Exp $
%%

-module(wings_vec).

-export([init/0,do_ask/3]).

-define(NEED_OPENGL, 1).
-define(NEED_ESDL, 1).
-include("wings.hrl").

-import(lists, [foldl/3,keydelete/3,reverse/1,member/2,last/1]).

-record(ss, {f,					%Fun.
	     selmodes,				%Legal selection modes.
	     is_axis=false,			%True if axis.
	     info="",				%Info message.
	     vec=none,				%Current vector.
	     cb,				%Callback
	     mag=false				%Magnet possible or not.
	    }).

%% Keep the display for the current vector.
-record(vec,
	{vec,					%Display list for vector.
	 src_vec=undefined			%Source for vector.
	}).

init() ->
    DefAxis = {{0.0,0.0,0.0},{1.0,0.0,0.0}},
    wings_pref:set_default(last_axis, DefAxis),
    wings_pref:set_default(default_axis, DefAxis),
    wings_pref:set_default(magnet_type, dome),
    wings_pref:set_default(magnet_distance_route, shortest),
    wings_pref:set_default(magnet_radius, 1.0).

%% Call wings:ask/3; DO NOT call this function directly.
do_ask({Do,Done}, St, Cb) ->
    Modes = [vertex,edge,face],
    do_ask_1(Modes, Do, Done, [], St, Cb);
do_ask({Do,Done,Flags}, St, Cb) ->
    Modes = [vertex,edge,face],
    do_ask_1(Modes, Do, Done, Flags, St, Cb);
do_ask({Do,Done,Flags,Modes}, St, Cb) ->
    do_ask_1(Modes, Do, Done, Flags, St, Cb).

do_ask_1(_, [], Res, _, _, Cb) ->
    wings_wm:later(build_result(Res, Cb)),
    keep;
do_ask_1(Modes, Do0, Done, Flags, St, Cb) ->
    Do = add_help_text(Do0),
    Mag = member(magnet, Flags),
    Ss = #ss{cb=Cb,mag=Mag,selmodes=Modes,f=fun(_, _) -> keep end},
    wings_wm:later({ask_init,Do,Done}),
    erase_vector(),
    {seq,push,get_event(Ss, St)}.

add_help_text([{_,_}=Pair|T]) ->
    [Pair|add_help_text(T)];
add_help_text([Type|T]) ->
    Val = {Type,
	   case Type of
	       axis_point -> "Pick axis (and point)";
	       axis -> "Pick axis";
	       point -> "Pick point";
	       magnet -> "Pick outer boundary point for magnet influence";
	       _ -> []
	   end},
    [Val|add_help_text(T)];
add_help_text([]) -> [].

magnet_possible_now(_, #ss{mag=false}) -> no;
magnet_possible_now([], _) -> inactive;
magnet_possible_now(Pl, _) ->
    case last(Pl) of
	{magnet,_} -> active;
	_ -> inactive
    end.

common_message(Msg, More, MagnetPossible) ->
    Rmb = case More of
	      [] -> "Execute";
	      [_|_] -> "Continue"
	  end,
    Message = wings_util:join_msg(wings_util:button_format(Msg, [], Rmb),
				  common_magnet_message(MagnetPossible)),
    wings_wm:message(Message, "").

common_magnet_message(no) -> [];
common_magnet_message(inactive) ->
    [$\s,wings_util:rmb_format("Magnet")];
common_magnet_message(active) ->
    ["  "|wings_util:magnet_string()].

magnet_message(Msg) ->
    Message = wings_util:join_msg(wings_util:button_format(Msg, [], "Execute "),
				  wings_util:rmb_format("Magnet options")),
    wings_wm:message(Message, "").

mode_restriction(Modes, #st{selmode=Mode}=St) ->
    wings:mode_restriction(Modes),
    case member(Mode, Modes) of
	true -> St;
	false -> St#st{selmode=last(Modes)}
    end.

pick_finish() ->
    wings_wm:dirty(),
    wings_draw_util:map(fun clear_orig_sel/2, []).

clear_orig_sel(D, _) -> D#dlo{sel=none,orig_sel=none,orig_mode=none}.

clear_sel() ->
    wings_wm:dirty(),
    wings_draw_util:map(fun clear_sel/2, []).

clear_sel(D, _) -> D#dlo{sel=none}.

%%%
%%% Event handler for secondary selection mode.
%%%

get_event(Ss, St) ->
    wings_draw:update_dlists(St),
    wings_wm:dirty(),
    {replace,fun(Ev) -> handle_event(Ev, Ss, St) end}.

handle_event({ask_init,Do,Done}, #ss{selmodes=Modes}=Ss, #st{selmode=Mode}=St0) ->
    wings_draw_util:map(fun(#dlo{orig_sel=none,sel=Dlist}=D, _) ->
				D#dlo{orig_sel=Dlist,orig_mode=Mode}
			end, []),
    wings_util:menu_restriction(wings_wm:this(), [view,select]),
    St = wings_sel:reset(mode_restriction(Modes, St0)),
    pick_next(Do, Done, Ss, St);
handle_event(Event, Ss, St) ->
    case wings_camera:event(Event, St) of
	next -> handle_event_1(Event, Ss, St);
	Other -> Other
    end.

handle_event_1(Event, Ss, St) ->
    case wings_pick:event(Event, St, fun() -> redraw(Ss, St) end) of
	next -> handle_event_2(Event, Ss, St);
	Other -> Other
    end.

handle_event_2(#mousebutton{x=X,y=Y}=Ev0, Ss, St0) ->
    case wings_menu:is_popup_event(Ev0) of
	{yes,Xglobal,Yglobal,Mod} ->
	    case wings_pick:do_pick(X, Y, St0) of
		{add,_,St} ->
		    Ev = wings_wm:local2global(Ev0),
		    wings_io:putback_event(Ev),
		    wings_wm:later({new_state,St});
		_ ->
		    exit_menu(Xglobal, Yglobal, Mod, Ss, St0)
	    end;
	no -> handle_event_3(Ev0, Ss, St0)
    end;
handle_event_2(Ev, Ss, St) -> handle_event_3(Ev, Ss, St).

handle_event_3(#keyboard{}=Ev, Ss, St0) ->
    case handle_key(Ev, Ss, St0) of
	next ->
	    case wings_hotkey:event(Ev, St0) of
		next -> handle_event_4(Ev, Ss, St0);
		{Menu,_}=Act when Menu == view; Menu == select->
		    wings_io:putback_event({action,Act}),
		    keep;
		_Other -> keep
	    end;
	Other -> Other
    end;
handle_event_3(Ev, Ss, St) -> handle_event_4(Ev, Ss, St).

handle_event_4({new_state,St}, #ss{f=Check}=Ss, _St0) ->
    case Check(check, St) of
	{Vec,Msg} -> 
	    get_event(Ss#ss{info=Msg,vec=Vec}, St);
	[{Vec,Msg}|_] ->
	    get_event(Ss#ss{info=Msg,vec=Vec}, St)
    end;
handle_event_4(redraw, Ss, St) ->
    redraw(Ss, St),
    keep;
handle_event_4({action,{select,Cmd}}, Ss, St0) ->
    case wings_sel_cmd:command(Cmd, St0) of
	St0 -> keep;
	{save_state,St} -> filter_sel_command(Ss, St);
	St -> filter_sel_command(Ss, St)
    end;
handle_event_4({action,{view,auto_rotate}}, _, _) ->
    keep;
handle_event_4({action,{view,Cmd}}, Ss, St0) ->
    St = wings_view:command(Cmd, St0),
    get_event(Ss, St);
handle_event_4({action,{secondary_selection,abort}}, _, _) ->
    erase_vector(),
    wings_wm:later(revert_state),
    pick_finish(),
    pop;
handle_event_4(quit, _Ss, _St) ->
    erase_vector(),
    wings_io:putback_event(quit),
    pop;
handle_event_4(init_opengl, _, St) ->
    erase_vector(),
    wings:init_opengl(St);
handle_event_4(_Event, Ss, St) ->
    get_event(Ss, St).

pick_next(Do, Done, #ss{is_axis=true,vec={{_,_,_},{_,_,_}}=Vec}=Ss, St) ->
    wings_pref:set_value(last_axis, Vec),
    pick_next_1(Do, Done, Ss, St);
pick_next(Do, Done, Ss, St) -> pick_next_1(Do, Done, Ss, St).

pick_next_1([], Res, #ss{cb=Cb}, _) ->
    pick_finish(),
    wings:clear_mode_restriction(),
    wings_wm:later(build_result(Res, Cb)),
    pop;
pick_next_1([{Fun0,Desc}|More], _Done, Ss, St) when is_function(Fun0) ->
    Fun = fun(message, _) -> common_message(Desc, More, no);
	     (A, B) -> Fun0(A, B)
	  end,
    get_event(Ss#ss{f=Fun,is_axis=false,vec=none,info=""}, wings_sel:reset(St));
pick_next_1([{Type,Desc}|More], Done, Ss, St0) ->
    MagnetPossible = magnet_possible_now(More, Ss),
    Check = case Type of
		magnet -> magnet;
		point -> fun check_point/1;
		axis -> fun check_vector/1;
		axis_point -> fun check_vector/1
	    end,
    Fun = case Type of
	      magnet ->
		  fun(check, S) ->
			  check_magnet_point(S);
		     (exit, {Mod,Vec,_}) ->
			  exit_magnet(Vec, Mod, Done);
		     (message, _) ->
			  magnet_message(Desc)
		  end;
	      _ ->
		  fun(check, S) ->
			  Check(S);
		     (exit, {Mod,Vec,S}) ->
			  common_exit(Type, Vec, Mod, More, Done,
				      MagnetPossible, S);
		     (message, _) ->
			  common_message(Desc, More, MagnetPossible)
		  end
	  end,
    IsAxis = (Type =/= point andalso Type =/= magnet),
    St = case Type of
	     magnet -> St0#st{selmode=vertex};
	     _ -> St0
	 end,
    clear_sel(),
    get_event(Ss#ss{f=Fun,is_axis=IsAxis,vec=none,info=""}, wings_sel:reset(St)).

redraw(#ss{info=Info,f=Message,vec=Vec}, St) ->
    Message(message, St),
    wings:redraw(Info, St),
    gl:pushAttrib(?GL_CURRENT_BIT bor ?GL_ENABLE_BIT bor
		  ?GL_TEXTURE_BIT bor ?GL_POLYGON_BIT bor
		  ?GL_LINE_BIT bor ?GL_COLOR_BUFFER_BIT bor
		  ?GL_LIGHTING_BIT),
    wings_view:load_matrices(false),
    draw_vec(Vec),
    gl:popAttrib(),
    wings_wm:current_state(St).

filter_sel_command(#ss{selmodes=Modes}=Ss, #st{selmode=Mode}=St) ->
    case member(Mode, Modes) of
	true -> handle_event({new_state,St}, Ss, St);
	false -> keep
    end.

handle_key(#keyboard{sym=$1}, _, St) ->	%1
    wings_io:putback_event({new_state,St}),
    keep;
handle_key(#keyboard{sym=$2}, #ss{f=Check}=Ss, St) -> %2
    case Check(check, St) of
	{Vec,Msg} -> 
	    get_event(Ss#ss{info=Msg,vec=Vec}, St);
	[_,{Vec,Msg}|_] ->
	    get_event(Ss#ss{info=Msg,vec=Vec}, St);
	[{Vec,Msg}] ->
	    get_event(Ss#ss{info=Msg,vec=Vec}, St)
    end;
handle_key(#keyboard{sym=27}, _, _) ->		%Escape
    wings_wm:later({action,{secondary_selection,abort}});
handle_key(_, _, _) -> next.

exit_menu(X, Y, Mod, #ss{f=Exit,vec=Vec}=Ss, St) ->
    case Exit(exit, {Mod,Vec,St}) of
	error ->
	    Menu = [{"Cancel",abort,"Cancel current command"}],
	    wings_menu:popup_menu(X, Y, secondary_selection, Menu);
	keep ->
	    keep;
	{Do,Done} ->
	    pick_next(Do, Done, Ss, St)
    end.

common_exit(_, none, _, _, _, _, _) ->
    error;
common_exit(Type, Vec, Mod, More, Acc, inactive, _St) ->
    RmbMod = wings_camera:free_rmb_modifier(),
    if
	Mod band RmbMod =:= 0 ->
	    common_exit_1(Type, Vec, More, Acc);
	More =:= [] ->
	    common_exit_1(Type, Vec, add_magnet(More), Acc);
	true ->
	    case last(More) of
		{magnet,_} -> common_exit_1(Type, Vec, More, Acc);
		_  -> common_exit_1(Type, Vec, add_magnet(More), Acc)
	    end
    end;
common_exit(Type, Vec, _, More, Acc, _, _) ->
    common_exit_1(Type, Vec, More, Acc).

common_exit_1(axis, {_,Vec}, PickList, Acc) ->
    {PickList,add_to_acc(Vec, Acc)};
common_exit_1(axis_point, {Point,Vec}, PickList, Acc0) ->
    Acc = add_to_acc(Point, add_to_acc(Vec, Acc0)),
    {PickList,Acc};
common_exit_1(point, Point, PickList, Acc) ->
    {PickList,add_to_acc(Point, Acc)}.

add_magnet(More) ->
    More ++ [{magnet,"Pick outer boundary point for magnet influence"}].

add_to_acc(Vec, [radial]) -> [{radial,Vec}];
add_to_acc(Vec, Acc) -> [Vec|Acc].

build_result([Res], Cb) ->
    build_result_1(Res, Cb);
build_result(Res, Cb) ->
    build_result_1(list_to_tuple(reverse(Res)), Cb).

build_result_1(Res, Cb) ->
    {command,fun(St) -> Cb(Res, St#st{ask_args=Res}) end}.

%%%
%%% Vector functions.
%%%

check_vector(#st{sel=[]}) -> {none,""};
check_vector(#st{selmode=Mode,sel=[{Id,Elems0}],shapes=Shs}) ->
    We = gb_trees:get(Id, Shs),
    Elems = gb_sets:to_list(Elems0),
    get_vec(Mode, Elems, We);
check_vector(_) -> {none,"Select parts of one object only"}.

%% Use single edge as axis
get_vec(edge, [Edge], #we{es=Etab,vp=Vtab}=We) ->
    #edge{vs=Va,ve=Vb,lf=Lf,rf=Rf} = gb_trees:get(Edge, Etab),
    VaPos = gb_trees:get(Va, Vtab),
    VbPos = gb_trees:get(Vb, Vtab),
    Vec = e3d_vec:norm(e3d_vec:sub(VbPos, VaPos)),
    Center = wings_vertex:center([Va,Vb], We),
    Ln = wings_face:normal(Lf, We),
    Rn = wings_face:normal(Rf, We),
    Normal = e3d_vec:norm(e3d_vec:add(Ln, Rn)),
    [{{Center,Vec},"Edge saved as axis (press \"2\" to save edge normal)."},
     {{Center,Normal},
      "Edge normal saved as axis (press \"1\" to save edge direction)."}];
%% Use direction between two edges
get_vec(edge, [Edge1,Edge2], #we{es=Etab,vp=Vtab}) ->
    #edge{vs=Va1,ve=Vb1} = gb_trees:get(Edge1, Etab),
    #edge{vs=Va2,ve=Vb2} = gb_trees:get(Edge2, Etab),
    Va1Pos = gb_trees:get(Va1, Vtab),
    Vb1Pos = gb_trees:get(Vb1, Vtab),
    Va2Pos = gb_trees:get(Va2, Vtab),
    Vb2Pos = gb_trees:get(Vb2, Vtab),
    Center1 = e3d_vec:average([Va1Pos,Vb1Pos]),
    Center2 = e3d_vec:average([Va2Pos,Vb2Pos]),
    Center = e3d_vec:average([Center1,Center2]),
    Vec = e3d_vec:norm(e3d_vec:sub(Center1, Center2)),
    [{{Center,Vec},"Direction between edges saved as axis."}];
%% Use edge-loop normal.
get_vec(edge, Edges, #we{vp=Vtab}=We) ->
    case wings_edge_loop:edge_loop_vertices(Edges, We) of
	[Vs] -> 
	    Center = wings_vertex:center(Vs, We),
	    Vec = wings_face:face_normal_ccw(Vs, Vtab),
	    [{{Center,Vec},"Edge loop normal saved as axis."}];
	_Other ->
	    {none,"Multi-edge selection must form a single closed edge loop."}
    end;

%% Vertex normal
get_vec(vertex, [V], We) ->
    Vec = wings_vertex:normal(V, We),
    Center = wings_vertex:center([V], We),
    [{{Center,Vec},"Vertex normal saved."}];
%% Direction between 2 vertices as axis
get_vec(vertex, [Va,Vb]=Vs, We) ->
    VaPos = wings_vertex:pos(Va, We),
    VbPos = wings_vertex:pos(Vb, We),
    Vec = e3d_vec:norm(e3d_vec:sub(VaPos, VbPos)),
    Center = wings_vertex:center(Vs, We),
    Normal = e3d_vec:norm(e3d_vec:add(wings_vertex:normal(Va, We),
				      wings_vertex:normal(Vb, We))),
    [{{Center,Vec},"Direction between vertices saved as axis "
      "(press \"2\" to save average of vertex normals)."},
     {{Center,Normal},"Average of vertex normals saved as axis "
      "(press \"1\" to save direction between vertices)."}];

%% 3-point (defines face) perpendicular
get_vec(vertex, [_,_,_]=Vs, #we{vp=Vtab}=We) ->
    Vec = wings_face:face_normal_ccw(Vs, Vtab),
    Center = wings_vertex:center(Vs, We),
    [{{Center,Vec},"3-point perp. normal saved as axis."}];
%% Take the edge loop normal.
get_vec(vertex, Vs0, #we{vp=Vtab}=We) ->
    Edges = find_edges(Vs0, We),
    case wings_edge_loop:edge_loop_vertices(Edges, We) of
	[Vs] -> 
	    Center = wings_vertex:center(Vs, We),
	    Vec = wings_face:face_normal_cw(Vs, Vtab),
	    [{{Center,Vec},"Edge loop normal saved as axis."}];
	_Other ->
	    {none,"Multi-vertex selection must form a single closed edge loop."}
    end;

%% Face normal
get_vec(face, [Face], We) ->
    Vec = wings_face:normal(Face, We),
    Vs = wings_face:to_vertices([Face], We),
    Center = wings_vertex:center(Vs, We),
    [{{Center,Vec},"Face normal saved as axis."}];
%% Direction between two faces as axis
get_vec(face, [Face1,Face2], We) ->
    Center1 = wings_face:center(Face1, We),
    Center2 = wings_face:center(Face2, We),
    Center = e3d_vec:average([Center1,Center2]),
    Vec = e3d_vec:norm(e3d_vec:sub(Center1, Center2)),
    Face1n = wings_face:normal(Face1, We),
    Face2n = wings_face:normal(Face2, We),
    Normal = e3d_vec:norm(e3d_vec:add(Face1n, Face2n)),
    [{{Center,Vec},"Direction between face centers saved as axis "
      "(press \"2\" to save average of face normals)."},
     {{Center,Normal},"Average of face normals saved as axis "
      "(press \"1\" to save direction between face centers)."}];
get_vec(face, Faces, #we{vp=Vtab}=We) ->
    case wings_vertex:outer_partition(Faces, We) of
	[Vs] ->
	    Center = wings_vertex:center(Vs, We),
	    Vec = wings_face:face_normal_cw(Vs, Vtab),
	    [{{Center,Vec},"Edge loop normal for region saved as axis."}];
	_Other ->
	    {none,"Multi-face selection must have a single edge loop."}
    end;

get_vec(_, _, _) -> {none,"Select vertices, edges, or faces."}.

%%%
%%% Point functions.
%%%

check_point(#st{sel=[]}) -> {none,""};
check_point(St) ->
    case kill_mirror(St) of
	St ->
	    Center = e3d_vec:average(wings_sel:bounding_box(St)),
	    [{Center,"Midpoint of selection saved."}];
	NoMirror ->
	    Center = e3d_vec:average(wings_sel:bounding_box(St)),
	    NoMirrorCenter = e3d_vec:average(wings_sel:bounding_box(NoMirror)),
	    [{Center,"Midpoint of selection saved "
	      "(press \"2\" to disregard virtual mirror in "
	      "reference point calculation)"},
	     {NoMirrorCenter,"Midpoint of selection saved "
	      "(press \"1\" to include virtual mirror in " 
	      "reference point calculation)"}]
    end.

check_magnet_point(#st{sel=[]}) -> {none,""};
check_magnet_point(St0) ->
    St = kill_mirror(St0),
    Center = e3d_vec:average(wings_sel:bounding_box(St)),
    [{Center,"Midpoint of selection saved."}].

kill_mirror(#st{shapes=Shs0}=St) ->
    Shs = kill_mirror_1(gb_trees:values(Shs0), []),
    St#st{shapes=Shs}.

kill_mirror_1([#we{id=Id,mirror=none}=We|Wes], Acc) ->
    kill_mirror_1(Wes, [{Id,We}|Acc]);
kill_mirror_1([#we{id=Id}=We|Wes], Acc) ->
    kill_mirror_1(Wes, [{Id,We#we{mirror=none}}|Acc]);
kill_mirror_1([], Acc) ->
    gb_trees:from_orddict(reverse(Acc)).

%%%
%%% Magnet functions.
%%%

exit_magnet(Vec, Mod, Acc) ->
    case wings_camera:free_rmb_modifier() of
	ModRmb when Mod band ModRmb =/= 0 ->
	    Fun = fun(Mag) ->
			  {[],[Mag|Acc]}
		  end,
	    case Vec  of
		none ->
		    wings_magnet:dialog(Fun);
		Point ->
		    wings_magnet:dialog(Point, Fun)
	    end;
	_ ->
	    case Vec of
		none ->
		    error;
		Point ->
		    Mag = {magnet,wings_pref:get_value(magnet_type),
			   wings_pref:get_value(magnet_distance_route),Point},
		    {[],[Mag|Acc]}
	    end
    end.

%%%
%%% Utilities.
%%%

find_edges(Vs, We) ->
    VsSet = gb_sets:from_list(Vs),
    Es = find_edges(Vs, VsSet, We, []),
    ordsets:from_list(Es).

find_edges([V|Vs], VsSet, We, Acc0) ->
    Acc = wings_vertex:fold(
	    fun(E, _, Rec, A) ->
		    OtherV = wings_vertex:other(V, Rec),
		    case gb_sets:is_member(OtherV, VsSet) of
			false -> A;
			true -> [E|A]
		    end
	    end, Acc0, V, We),
    find_edges(Vs, VsSet, We, Acc);
find_edges([], _VsSet, _We, Acc) -> Acc.

%%%
%%% Showing of active vector.
%%%

erase_vector() ->
    case erase(wings_current_vector) of
	undefined -> ok;
	#vec{vec=VecDl} -> catch gl:deleteLists(VecDl, 1)
    end.

draw_vec(none) ->
    erase_vector();
draw_vec(Vec) ->
    case get(wings_current_vector) of
	#vec{src_vec=Vec,vec=VecDl} -> ok;
	_ ->
	    erase_vector(),
	    VecDl = make_vec_dlist(Vec),
	    put(wings_current_vector, #vec{vec=VecDl,src_vec=Vec}),
	    VecDl
    end,
    wings_draw_util:call(VecDl).

make_vec_dlist(Vec) ->
    Dlist = gl:genLists(1),
    gl:newList(Dlist, ?GL_COMPILE),
    make_vec_dlist_1(Vec),
    gl:endList(),
    Dlist.

make_vec_dlist_1({Center,Vec0}) ->
    Vec = e3d_vec:mul(Vec0, wings_pref:get_value(active_vector_size)),
    End = e3d_vec:add(Center,Vec),
    HeadVec = e3d_vec:mul(Vec, -0.2),
    HeadPt = e3d_vec:add(End, HeadVec),
    case HeadVec of
	{Same,Same,Same} ->
	    PosHead0 = e3d_vec:cross(HeadVec, {0.25,-0.25,0.25}),
	    PosHead1 = e3d_vec:cross(HeadVec, {-0.25,0.25,-0.25});
	_Other ->
	    PosHead0 = e3d_vec:cross(HeadVec, {0.25,0.25,0.25}),
	    PosHead1 = e3d_vec:cross(HeadVec, {-0.25,-0.25,-0.25})
    end,
    Width = wings_pref:get_value(active_vector_width),
    gl:color3fv(wings_pref:get_value(active_vector_color)),
    gl:pointSize(Width*3.5),
    gl:lineWidth(Width),
    gl:'begin'(?GL_LINES),
    gl:vertex3fv(Center),
    gl:vertex3fv(End),
    gl:vertex3fv(End),
    gl:vertex3fv(e3d_vec:sub(HeadPt, PosHead0)),
    gl:vertex3fv(End),
    gl:vertex3fv(e3d_vec:sub(HeadPt, PosHead1)),
    gl:'end'();
make_vec_dlist_1(Center) ->
    Width = wings_pref:get_value(active_vector_width),
    gl:color3fv(wings_pref:get_value(active_vector_color)),
    gl:pointSize(Width*3.5),
    gl:'begin'(?GL_POINTS),
    gl:vertex3fv(Center),
    gl:'end'().
