1%%
2%%  wpc_intersect_vertex.erl --
3%%
4%%     Plug-in for moving vertice(s) to the intersection of a line and plane
5%%
6%%  Copyright (c) 2004-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%%  Contributed by elrond79.
12%%
13%%     $Id$
14%%
15%%  2000-10-01:  Changed help text to incorporate suggestions by Puzzled Paul
16%%  2000-09-21:  Normalized LD and PN (so dot product not <.001 if either is very
17%%               short)
18%%  2000-09-17:  Tried to make more compliant w/ wings API (Paul Molodowitch)
19
20-module(wpc_intersect_vertex).
21
22-export([init/0,menu/2,command/2,intersect_vertex/3]).
23
24-include_lib("wpc_intersect.hrl").
25-import(lists, [foldl/3,splitwith/2]).
26
27init() ->
28    true.
29
30
31% If we can find the "flatten" menu item, stick it in after that; otherwise, put at end
32menu({vertex}, Menu) ->
33    {SplitMenu1, SplitMenu2} = splitwith(fun isNotFlattenMenuItem/1, Menu),
34    if
35	length(SplitMenu1) < length(SplitMenu2) ->
36	    [FlattenMenuItem|MenuTail] = SplitMenu2,
37	    SplitMenu1 ++ [FlattenMenuItem|menu_item()] ++ MenuTail;
38	true ->
39	    Menu ++ [separator] ++ menu_item()
40    end;
41menu(_,Menu) -> Menu.
42
43isNotFlattenMenuItem({"Flatten",_}) ->
44    false;
45isNotFlattenMenuItem({_,{flatten,_}}) ->
46    false;
47isNotFlattenMenuItem(_) ->
48    true.
49
50
51command({vertex,{intersect,{Type,{'ASK',Ask}}}}, St) ->
52    intersect({Type,{'ASK',Ask}}, St);
53%% command for repeat drag args
54command({vertex,{intersect,{Type,Data}}},St) ->
55    intersect_ask_callback({Type,Data},St);
56
57command(_,_) -> next.
58
59%% creates the menu item for the vertex intersect command.
60
61menu_item() ->
62    [{?__(8,"Intersect"),{intersect,fun adv_submenu/2}}].
63
64submenu_items(1) ->
65    {stay_on_line,
66     {'ASK',{[{axis,       ?__(1,"Pick direction of line - line will pass through selected vertex(es)")},
67	      {axis_point, ?__(2,"Pick plane to intersect")}],[],[]}}};
68submenu_items(2) ->
69    {stay_on_plane,
70     {'ASK',{[{axis,       ?__(3,"Pick plane normal - plane will pass through selected vertex(es)")},
71	      {axis_point, ?__(4,"Pick line to intersect")}],[],[]}}};
72submenu_items(3) ->
73    {pick_all,
74     {'ASK',{[{axis,       ?__(5,"Pick direction of line")},
75	      {point,      ?__(6,"Pick point for line to pass through")},
76	      {axis,       ?__(7,"Pick plane normal")},
77	      {point,      ?__(8,"Pick point for plane to pass through")}],[],[]}}}.
78
79adv_submenu(help, _) ->
80    {?__(1,"Stay on line, move to intersection with plane"),
81     ?__(2,"Stay on plane, move to line"),
82     ?__(3,"Pick line and plane")};
83adv_submenu(Button, NS) ->
84    wings_menu:build_command(submenu_items(Button), NS).
85
86intersect({stay_on_line, {'ASK',Ask}}, St0) ->
87    wings:ask(Ask, St0, fun (AskResult, St) -> intersect_ask_callback({stay_on_line, AskResult}, St) end);
88intersect({stay_on_plane, {'ASK',Ask}}, St0) ->
89    wings:ask(Ask, St0, fun (AskResult, St) -> intersect_ask_callback({stay_on_plane, AskResult}, St) end);
90intersect({pick_all, {'ASK',Ask}}, St0) ->
91    wings:ask(Ask, St0, fun (AskResult, St) -> intersect_ask_callback({pick_all, AskResult}, St) end).
92
93intersect_ask_callback({stay_on_line, {LineDir, PlaneNorm, PlanePoint}}, St) ->
94    intersect(LineDir, selection, PlaneNorm, PlanePoint, St);
95intersect_ask_callback({stay_on_plane, {PlaneNorm, LineDir, LinePoint}}, St) ->
96    intersect(LineDir, LinePoint, PlaneNorm, selection, St);
97intersect_ask_callback({pick_all, {LineDir, LinePoint, PlaneNorm, PlanePoint}}, St) ->
98    intersect(LineDir, LinePoint, PlaneNorm, PlanePoint, St).
99
100
101intersect(LineDir0, LinePoint, PlaneNorm0, PlanePoint, St) ->
102    PlaneNorm = e3d_vec:norm(PlaneNorm0),
103    LineDir = e3d_vec:norm(LineDir0),
104    DotProd = e3d_vec:dot(LineDir, PlaneNorm),
105    if
106 	abs(DotProd) > 0.001 ->
107	    IntersectData = #intersect_data{lineDir = LineDir, linePoint = LinePoint,
108					    planeNorm = PlaneNorm, planePoint = PlanePoint,
109					    lineDotPlane = DotProd},
110	    {save_state,
111	     wpa:sel_map(
112	       fun(Vs, We) ->
113		       intersect_body(Vs, We, IntersectData)
114	       end, St)};
115	true ->
116 	    wpa:error_msg(?__(1,"Line and plane are nearly parallel:\n"
117		      "can't find intersection.")),
118	    keep
119    end.
120
121intersect_body(Vs, #we{vp=Vtab0}=We0, #intersect_data{} = IntersectData) when is_list(Vs) ->
122    Vtab = foldl(fun(V, VTabAcc) ->
123  			 intersect_vertex(V, VTabAcc, IntersectData)
124  		 end, Vtab0, Vs),
125    We = We0#we{vp=Vtab},
126    wings_we:mirror_flatten(We0, We);
127intersect_body(Vs, We, #intersect_data{} = IntersectData) ->
128    intersect_body(gb_sets:to_list(Vs), We, IntersectData).
129
130
131intersect_vertex(V, Tab, #intersect_data{linePoint = selection} = IntersectData0) ->
132    IntersectData = IntersectData0#intersect_data{linePoint = array:get(V, Tab)},
133    intersect_vertex(V, Tab, IntersectData);
134intersect_vertex(V, Tab, #intersect_data{planePoint = selection} = IntersectData0) ->
135    IntersectData = IntersectData0#intersect_data{planePoint = array:get(V, Tab)},
136    intersect_vertex(V, Tab, IntersectData);
137intersect_vertex(V, Tab, #intersect_data{lineDir = LD, linePoint = LP,
138					  planeNorm = PN, planePoint = PP, lineDotPlane = LDdotPN}) ->
139    %% The vector equation for the line is
140    %%       LP + xLD
141    %% for any scalar x; we need to find x st we have a point on the plane.
142    %% For any point P on the plane, we know that P - PP is perpendicular
143    %% to the plane normal, ie that
144    %%       (P - PP).PN = 0
145    %% So sticking in our line equation for P, we have
146    %%       (LP + xLD - PP).PN = 0   ->   x = (PP - LP).PN
147    %%                                         ------------
148    %%                                            LD.PN
149
150    X = e3d_vec:dot(e3d_vec:sub(PP, LP),PN)/LDdotPN,
151    Intersection = e3d_vec:add(LP, e3d_vec:mul(LD, X)),
152    array:set(V, Intersection, Tab).
153
154