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