1%%
2%%  wpc_ncube.erl --
3%%
4%%     N-Cube and N-Gon Plugin
5%%
6%%  Copyright (c) 2003-2011 Anthony D'Agostino
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%%
12
13-module(wpc_ncube).
14-export([init/0,menu/2,command/2]).
15-import(math, [cos/1,sin/1,pi/0]).
16-include_lib("src/wings.hrl").
17
18init() -> true.
19
20menu({shape}, []) ->
21    menu();
22menu({shape}, Menu) ->
23    [Cube|Ngon] = menu(),
24    [Cube] ++ Menu ++ [separator|Ngon];
25menu(_, Menu) -> Menu.
26
27menu() ->
28    [{cube_str(),ncube,?__(1,"Create a cube"),[option]},
29     {?__(2,"N-Gon"),ngon,[option]}].
30
31command({shape,{ncube, Arg}}, St) -> make_ncube(Arg, St);
32command({shape,{ngon, Arg}}, St) -> make_ngon(Arg, St);
33command(_, _) -> next.
34
35cube_str() ->
36    ?__(1,"Cube").
37
38%%% The rest are local functions.
39
40% =============
41% === Ncube ===
42% =============
43make_ncube(Arg, St) when is_atom(Arg) ->
44    wings_dialog:dialog_preview({shape,ncube}, Arg, ?__(1,"Cube Options"),
45				ncube_dialog(), St);
46make_ncube(Arg, _) ->
47    % set_pref(Arg),	% don't save
48    ArgDict = dict:from_list(Arg),
49    Nres = dict:fetch(nres, ArgDict),
50    X = dict:fetch(xcube, ArgDict)/2,
51    Y = dict:fetch(ycube, ArgDict)/2,
52    Z = dict:fetch(zcube, ArgDict)/2,
53    Rot_X = dict:fetch(rot_x, ArgDict),
54    Rot_Y = dict:fetch(rot_y, ArgDict),
55    Rot_Z = dict:fetch(rot_z, ArgDict),
56    Mov_X = dict:fetch(mov_x, ArgDict),
57    Mov_Y = dict:fetch(mov_y, ArgDict),
58    Mov_Z = dict:fetch(mov_z, ArgDict),
59    Ground = dict:fetch(ground, ArgDict),
60
61    SpherizeFlag = dict:fetch(spherizeflag, ArgDict),
62    Verts = ncube_verts(Nres+1),
63    Faces = ncube_faces(Nres+1, Nres+1),
64    {Vs0, Fs} = clean_indexed_mesh(Verts, Faces),
65    Vs1 = transform_mesh(SpherizeFlag, {X,Y,Z}, Vs0),
66    Vs = wings_shapes:transform_obj({Rot_X,Rot_Y,Rot_Z},{Mov_X,Mov_Y,Mov_Z},Ground, Vs1),
67    {new_shape,cube_str(),Fs,Vs}.
68
69ncube_dialog() ->
70    Nres = get_pref(nres, 1),
71    SpherizeFlag = get_pref(spherizeflag, false),
72    [{hframe,
73      [{slider, {text, Nres,
74		 [{key,nres},{range,{1,20}}]}}],[{title, ?__(1,"Number of Cuts")}]},
75        {hframe,[
76            {label_column, [
77                {wings_s:dir(x), {text,2.0,[{key,xcube},{range,{0.0,infinity}}]}},
78                {wings_s:dir(y), {text,2.0,[{key,ycube},{range,{0.0,infinity}}]}},
79                {wings_s:dir(z), {text,2.0,[{key,zcube},{range,{0.0,infinity}}]}}
80            ]}
81        ],[{margin,false}]},
82        {hradio,[{?__(2,"Yes"), true},
83                 {?__(3,"No"), false}],
84                SpherizeFlag, [{key,spherizeflag}, {title, ?__(4,"Spherize")}]},
85        wings_shapes:transform_obj_dlg()
86    ].
87
88ncube_verts(Nres) ->
89    S = 1.0,
90    Nverts = plane_verts(Nres),
91    Tverts = [{X, S, Z} || {X,Z} <- Nverts],
92    Bverts = [{X, -S, -Z} || {X,Z} <- Nverts],
93    Fverts = [{X, -Z, S} || {X,Z} <- Nverts],
94    Kverts = [{-X, -Z, -S} || {X,Z} <- Nverts],
95    Rverts = [{ S, -X, Z} || {X,Z} <- Nverts],
96    Lverts = [{-S,  X, Z} || {X,Z} <- Nverts],
97    VertsWithDups = Tverts ++ Bverts ++ Fverts ++ Kverts ++ Rverts ++ Lverts,
98    VertsWithDups.
99
100transform_mesh(false, Box, Vs) ->
101    [transform(Box,V) || V <- Vs];
102transform_mesh(true, Box, Vs) ->
103    [transform(Box,e3d_vec:norm(V)) || V <- Vs].
104
105transform({Xs,Ys,Zs}, {Xp,Yp,Zp}) ->
106    {Xp*Xs, Yp*Ys, Zp*Zs}.
107
108ncube_faces(Nres, Nres) ->
109    Nsq = Nres*Nres,
110    Tfaces = plane_faces(Nres, Nres),
111    Bfaces = side_faces(Nsq*1, Tfaces),
112    Ffaces = side_faces(Nsq*2, Tfaces),
113    Kfaces = side_faces(Nsq*3, Tfaces),
114    Rfaces = side_faces(Nsq*4, Tfaces),
115    Lfaces = side_faces(Nsq*5, Tfaces),
116    Faces = Tfaces ++ Bfaces ++ Ffaces ++ Kfaces ++ Rfaces ++ Lfaces,
117    Faces.
118
119side_faces(Offset, Faces) ->
120    AddOffset = fun([A,B,C,D]) -> [A+Offset,B+Offset,C+Offset,D+Offset] end,
121    lists:map(AddOffset, Faces).
122
123plane_verts(Nres) ->
124    [{dtc_round((I/(Nres-1)*2-1)), dtc_round((J/(Nres-1)*2-1))}
125      || I <- lists:seq(0, Nres-1), J <- lists:seq(0, Nres-1)].
126
127plane_faces(Ures, Vres) ->
128    [[I*Vres+J, I*Vres+J+1, (I+1)*Vres+J+1, (I+1)*Vres+J]
129      || I <- lists:seq(0, Vres-2), J <- lists:seq(0, Ures-2)].
130
131dtc_round(Float) ->
132    dtc_round(Float, 6).
133
134dtc_round(Float, Decimals) -> % Accurately rounds decimals - www.digithings.com
135    (round(Float * math:pow(10, Decimals))) / math:pow(10, Decimals).
136
137% =============
138% === N-Gon ===
139% =============
140make_ngon(Arg, St) when is_atom(Arg) ->
141    wings_dialog:dialog_preview({shape,ngon}, Arg, ?__(1,"N-Gon Options"), ngon_dialog(), St);
142make_ngon(Arg, _) ->
143    ArgDict = dict:from_list(Arg),
144    NumVerts = dict:fetch(numverts, ArgDict),
145    Radius = dict:fetch(radius, ArgDict),
146    Rot_X = dict:fetch(rot_x, ArgDict),
147    Rot_Y = dict:fetch(rot_y, ArgDict),
148    Rot_Z = dict:fetch(rot_z, ArgDict),
149    Mov_X = dict:fetch(mov_x, ArgDict),
150    Mov_Y = dict:fetch(mov_y, ArgDict),
151    Mov_Z = dict:fetch(mov_z, ArgDict),
152    Ground = dict:fetch(ground, ArgDict),
153
154    Vs1 = ngon_verts(NumVerts, Radius),
155    Vs = wings_shapes:transform_obj({Rot_X,Rot_Y,Rot_Z},{Mov_X,Mov_Y,Mov_Z},Ground, Vs1),
156    Fs = ngon_faces(NumVerts),
157    {new_shape,?__(2,"N-Gon"),Fs,Vs}.
158
159ngon_dialog() ->
160    NumVerts = get_pref(numverts, 5),
161    Radius = get_pref(radius, 1.0),
162    [{vframe, [
163        {label_column, [
164            {?__(3,"Number of Verts"), {slider, {text, NumVerts,
165                                                 [{key, numverts}, {range, {3, 20}}]}}},
166            {?__(4,"Radius"), {slider, {text, Radius, [{key, radius}, {range, {0.1, 20.0}}]}}}]
167        },
168        wings_shapes:transform_obj_dlg()]
169    }].
170
171ngon_verts(NumVerts, Radius) ->
172    Nres = NumVerts,
173    Delta = 2*pi()/Nres,
174    [{Radius*cos(I*Delta),
175      0.0,
176      Radius*sin(I*Delta)} || I <- lists:seq(0, Nres-1)].
177
178ngon_faces(NumVerts) ->
179    Nres = NumVerts,
180    BotFaces = lists:seq(0, Nres-1),
181    TopFaces = lists:reverse(BotFaces),
182    Faces = [TopFaces, BotFaces],
183    Faces.
184
185clean_indexed_mesh(Verts, Faces) ->
186    Raw = e3d_util:indexed_to_raw(Verts, Faces),
187    e3d_util:raw_to_indexed(Raw).
188
189get_pref(Key, Def) ->
190    wpa:pref_get(?MODULE, Key, Def).
191