PageRenderTime 39ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/ex11/src/rcube.erl

http://github.com/gebi/jungerl
Erlang | 362 lines | 257 code | 41 blank | 64 comment | 0 complexity | dabf027c956a42d3229c0d5c9a72d20b MD5 | raw file
Possible License(s): AGPL-1.0, JSON, LGPL-2.1, BSD-3-Clause
  1. -module(rcube).
  2. -author('tobbe@erix.ericsson.se').
  3. %%% --------------------------------------------------------------------
  4. %%% Created: 92-01-30 by Tobbe (tobbe@erix.ericsson.se)
  5. %%% One of my first programs in Erlang, making use of
  6. %%% PXW which was an interface to the Athena Widget set.
  7. %%% I wanted to try out some simple graphics concepts,
  8. %%% such as how to remove hidden lines before drawing them.
  9. %%% Since then I have used this program as little test
  10. %%% on how easy/good the graphics capabilities are for
  11. %%% various toolkits.
  12. %%%
  13. %%% Modified: 12 Aug 1997 by tobbe@cslab.ericsson.se
  14. %%% Transformed to use GS.
  15. %%%
  16. %%% Modified: 19 Feb 1999 by tnt@home.se
  17. %%% Transformed again to use the Erlang X11 binding.
  18. %%%
  19. %%% --------------------------------------------------------------------
  20. -export([start/0, start/1,init/1,kicker/2]).
  21. -import(matrix44,[multiply14/2,mk_rotate_matrix/2,mk_hcord/3,
  22. mk_translate_matrix/3]).
  23. -include("ex11.hrl").
  24. -define(KICK_INTERVAL, 10). % Time in (ms) between redraws
  25. -record(rcube,{window, % The main window
  26. draw_gc, % GC to draw the cube into the pixmap
  27. clear_gc, % GC to clear the pixmap
  28. pixmap, % The pixmap
  29. pix_w, % The pixmap width
  30. pix_h % Te pixmap height
  31. }).
  32. start() ->
  33. start("localhost").
  34. start(Host) ->
  35. spawn(?MODULE,init,[Host]).
  36. init(Host) ->
  37. {X,Rcube} = mk_window(Host),
  38. init_xyz(),
  39. Fig = figure(),
  40. Object = draw_cube(X,Rcube,Fig),
  41. Kicker = start_kicker(?KICK_INTERVAL),
  42. timer:sleep(1000),
  43. loop(X,Rcube,Fig,Kicker).
  44. init_xyz() ->
  45. put(x,2),
  46. put(y,2),
  47. put(z,2),
  48. put(cube_center,{90,70,50,1}).
  49. loop(X,Rcube,Fig,Kicker) ->
  50. receive
  51. {Kicker,kick} ->
  52. NewFig = redraw(X,Rcube,Fig),
  53. loop(X,Rcube,NewFig,Kicker);
  54. {ex11,Msg} ->
  55. dispatch(X,Rcube,Fig,Kicker,Msg);
  56. reset ->
  57. loop(X,Rcube,figure(),Kicker);
  58. start when Kicker==false ->
  59. K = start_kicker(?KICK_INTERVAL),
  60. loop(X,Rcube,Fig,K);
  61. stop when pid(Kicker) ->
  62. stop_kicker(Kicker),
  63. loop(X,Rcube,Fig,false);
  64. {x,X} ->
  65. put(x,X),
  66. loop(X,Rcube,Fig,Kicker);
  67. {y,Y} ->
  68. put(y,Y),
  69. loop(X,Rcube,Fig,Kicker);
  70. {z,Z} ->
  71. put(z,Z),
  72. loop(X,Rcube,Fig,Kicker);
  73. quit ->
  74. exit(normal);
  75. geo ->
  76. %% Just to test that replies work
  77. R = #get_geometry{drawable=Rcube#rcube.window},
  78. ex11:req(X,R),
  79. loop(X,Rcube,Fig,Kicker);
  80. XX ->
  81. io:format("Got: ~p~n",[XX]),
  82. loop(X,Rcube,Fig,Kicker)
  83. end.
  84. %% -------------------------------------------
  85. %% Print out the events we get (just testing)
  86. dispatch(X,Rcube,Fig,Kicker,E) when ?IS_EXPOSE_EVENT(E) ->
  87. io:format("Got expose-event: x=~w y=~w width=~w height=~w count=~w~n",
  88. [E#expose.x,E#expose.y,E#expose.width,E#expose.height,E#expose.count]),
  89. loop(X,Rcube,Fig,Kicker);
  90. dispatch(X,Rcube,Fig,Kicker,E) when ?IS_CONFIGURE_NOTIFY_EVENT(E) ->
  91. io:format("Got configure_notify-event: x=~w y=~w width=~w height=~w~n",
  92. [E#configure_notify.x,E#configure_notify.y,
  93. E#configure_notify.width,E#configure_notify.height]),
  94. loop(X,Rcube,Fig,Kicker);
  95. dispatch(X,Rcube,Fig,Kicker,E) when ?IS_REPARENT_NOTIFY_EVENT(E) ->
  96. io:format("Got reparent_notify-event: x=~w y=~w override_redirect=~w~n",
  97. [E#reparent_notify.x,E#reparent_notify.y,E#reparent_notify.override_redirect]),
  98. loop(X,Rcube,Fig,Kicker);
  99. dispatch(X,Rcube,Fig,Kicker,E) when ?IS_MAP_NOTIFY_EVENT(E) ->
  100. io:format("Got map_notify-event ~n",[]),
  101. loop(X,Rcube,Fig,Kicker);
  102. dispatch(X,Rcube,Fig,Kicker,E) ->
  103. io:format("Got unknown event: ~w~n",[E]),
  104. loop(X,Rcube,Fig,Kicker).
  105. %% ------------------
  106. %% Redraw ye'ol cube
  107. redraw(X,Rcube,Fig) ->
  108. NewFig = do_transformations(Fig),
  109. draw_cube(X,Rcube,NewFig),
  110. NewFig.
  111. %% ------------------------------------------------
  112. %% Clear the pixmap and draw the cube in to it.
  113. %% Copy the pixmap to the displayed window.
  114. %% Return a list of the poly-line objects created.
  115. draw_cube(X,Rcube,Fig) ->
  116. clear_pixmap(X,Rcube),
  117. Object = draw_surfaces(X,Rcube,Fig,surfaces()),
  118. CopyArea = #copy_area{dst = Rcube#rcube.window,
  119. src = Rcube#rcube.pixmap,
  120. cid = Rcube#rcube.draw_gc,
  121. width = Rcube#rcube.pix_w,
  122. height = Rcube#rcube.pix_h
  123. },
  124. ex11:req(X,CopyArea),
  125. ex11:flush(X),
  126. Object.
  127. %% --------------------------------------------------
  128. %% Check for hidden surfaces and draw what's visible
  129. draw_surfaces(_,_,_,0) -> [];
  130. draw_surfaces(X,Rcube,Fig,Num) ->
  131. Points = surface(Num),
  132. {P1,P2,P3,P4} = Points,
  133. case hidden_surface_p(Points,Fig) of
  134. true ->
  135. draw_surfaces(X,Rcube,Fig,Num-1);
  136. false ->
  137. Plist = mk_point_list(Points,Fig),
  138. PolyLine = #poly_line{drawable = Rcube#rcube.pixmap,
  139. gc = Rcube#rcube.draw_gc,
  140. points = Plist
  141. },
  142. ex11:req(X,PolyLine),
  143. draw_surfaces(X,Rcube,Fig,Num-1)
  144. end.
  145. mk_point_list({P1,P2,P3,P4},Fig) ->
  146. {X1,Y1} = gp(P1,Fig),
  147. {X2,Y2} = gp(P2,Fig),
  148. {X3,Y3} = gp(P3,Fig),
  149. {X4,Y4} = gp(P4,Fig),
  150. First = #point{x=round(X1),y=round(Y1)},
  151. [%% First line
  152. First,
  153. #point{x=round(X2),y=round(Y2)},
  154. %% Second line
  155. #point{x=round(X2),y=round(Y2)},
  156. #point{x=round(X3),y=round(Y3)},
  157. %% Third line
  158. #point{x=round(X3),y=round(Y3)},
  159. #point{x=round(X4),y=round(Y4)},
  160. %% Fourth line
  161. #point{x=round(X4),y=round(Y4)},
  162. First].
  163. %% Get the X and Y coordinate for the specified point
  164. gp(1,[{X,Y,_,_}|_]) -> {X,Y};
  165. gp(N,[_|T]) -> gp(N-1,T).
  166. hidden_surface_p({P1,P2,P3,_},Fig) ->
  167. {X1,Y1} = gp(P1,Fig),
  168. {X2,Y2} = gp(P2,Fig),
  169. {X3,Y3} = gp(P3,Fig),
  170. C = X1*(Y2-Y3) + X2*(Y3-Y1) + X3*(Y1-Y2),
  171. if
  172. C > 0 -> false;
  173. true -> true
  174. end.
  175. %% --------------------------------
  176. %% Do the rotate transformations
  177. do_transformations(Fig) ->
  178. {Cx,Cy,Cz,_} = cube_center(),
  179. ToOrigo = cube_transform(Fig,mk_translate_matrix(-Cx,-Cy,-Cz)),
  180. Xtransformed = x_transform(ToOrigo),
  181. Ytransformed = y_transform(Xtransformed),
  182. Ztransformed = z_transform(Ytransformed),
  183. Back = cube_transform(Ztransformed,mk_translate_matrix(Cx,Cy,Cz)),
  184. hcord_to_cart(Back).
  185. x_transform(Cube) -> transform(Cube,x,get(x)).
  186. y_transform(Cube) -> transform(Cube,y,get(y)).
  187. z_transform(Cube) -> transform(Cube,z,get(z)).
  188. %% Only do the transform when Angle > 0
  189. transform(Cube,_,0) -> Cube;
  190. transform(Cube,Axis,Value) ->
  191. M = mk_rotate_matrix(Axis,Value),
  192. cube_transform(Cube,M).
  193. hcord_to_cart([]) -> [];
  194. hcord_to_cart([{X,Y,Z,S}|Tail]) when S==0 ->
  195. [{X,Y,Z,0}|hcord_to_cart(Tail)];
  196. hcord_to_cart([{X,Y,Z,S}|Tail]) ->
  197. [{X/S,Y/S,Z/S,1}|hcord_to_cart(Tail)].
  198. cube_transform([],_) -> [];
  199. cube_transform([Vector|Tail],Matrix) ->
  200. [multiply14(Vector,Matrix)|cube_transform(Tail,Matrix)].
  201. cube_center() -> get(cube_center).
  202. % Note that the order of the points is crucial,
  203. % in order to fit the definition of surfaces.
  204. figure() ->
  205. P1 = mk_hcord(80,60,40),
  206. P2 = mk_hcord(100,60,40),
  207. P3 = mk_hcord(100,80,40),
  208. P4 = mk_hcord(80,80,40),
  209. P5 = mk_hcord(80,60,60),
  210. P6 = mk_hcord(100,60,60),
  211. P7 = mk_hcord(100,80,60),
  212. P8 = mk_hcord(80,80,60),
  213. [P1,P2,P3,P4,P5,P6,P7,P8].
  214. surfaces() -> 6. % Number of surfaces
  215. % Defines the points a surface consists of
  216. surface(1) -> {4,3,2,1};
  217. surface(2) -> {3,7,6,2};
  218. surface(3) -> {8,7,3,4};
  219. surface(4) -> {5,8,4,1};
  220. surface(5) -> {6,7,8,5};
  221. surface(6) -> {1,2,6,5}.
  222. %% ------------------------
  223. %% Create the window stuff
  224. mk_window(Host) ->
  225. {ok,X} = ex11:start(Host),
  226. %%
  227. %% Get background and foreground from the Display
  228. %%
  229. {ok,Dpy} = ex11:get_display(X),
  230. White = ?WHITE_PIXEL(Dpy),
  231. Black = ?BLACK_PIXEL(Dpy),
  232. %%
  233. %% Create a top-level window
  234. %%
  235. Wmask = (?WIN_DEFAULT_VALUEMASK bor
  236. ?WIN_VALUEMASK_BG_PIXEL bor
  237. ?WIN_VALUEMASK_EVENT_MASK),
  238. Events = (?EVENT_EXPOSURE bor
  239. ?EVENT_STRUCTURE_NOTIFY),
  240. Wval = #win_values{bg_pixel = White,
  241. event_mask = Events
  242. },
  243. {ok,Window} = ex11:req(X,#create_window{width = 200,
  244. height = 200,
  245. value_mask = Wmask,
  246. value_list = Wval
  247. }),
  248. io:format("PIX=~w~n", [Window]),
  249. %%
  250. %% Create the Pixmap
  251. %%
  252. PixW = 120,
  253. PixH = 120,
  254. {ok,Pix} = ex11:req(X,#create_pixmap{drawable = Window,
  255. width = PixW,
  256. height = PixH
  257. }),
  258. %%
  259. %% Create the GC's
  260. %%
  261. LineWidth = 1,
  262. DrawGC = mk_draw_gc(X,Pix,LineWidth,White,Black),
  263. ClearGC = mk_clear_gc(X,Pix,White),
  264. %%
  265. %% Create the rcube datastructure
  266. %%
  267. Rcube = #rcube{window = Window,
  268. pixmap = Pix,
  269. draw_gc = DrawGC,
  270. clear_gc = ClearGC,
  271. pix_w = PixW,
  272. pix_h = PixH
  273. },
  274. %%
  275. %% Clear the pixmap
  276. %%
  277. clear_pixmap(X,Rcube),
  278. %%
  279. %% Show the window
  280. %%
  281. ex11:req(X,#map_window{window=Window}),
  282. {X,Rcube}.
  283. mk_clear_gc(X,Pix,Bg) ->
  284. mk_draw_gc(X,Pix,0,Bg,Bg).
  285. mk_draw_gc(X,Pix,LineW,Bg,Fg) ->
  286. GCmask = (?GC_DEFAULT_VALUEMASK bor
  287. ?GC_VALUEMASK_BACKGROUND bor
  288. ?GC_VALUEMASK_FOREGROUND),
  289. GCval = #gc_values{line_width=LineW,
  290. join_style=?GC_JOINSTYLE_BEVEL,
  291. exposures=?FALSE,
  292. background=Bg,
  293. foreground=Fg},
  294. {ok,Cid} = ex11:req(X,#create_gc{drawable=Pix,
  295. value_mask=GCmask,
  296. value_list=GCval}),
  297. Cid.
  298. clear_pixmap(X,R) ->
  299. Pix = R#rcube.pixmap,
  300. ClearGC = R#rcube.clear_gc,
  301. Rectangle = #rectangle{x=0,y=0,
  302. width=R#rcube.pix_w,
  303. height=R#rcube.pix_h},
  304. ex11:req(X,#poly_fill_rectangle{drawable=Pix,
  305. cid=ClearGC,
  306. rectangles=[Rectangle]}).
  307. %% -------------------
  308. %% THE KICKER PROCESS
  309. %% -------------------
  310. start_kicker(T) ->
  311. spawn_link(?MODULE,kicker,[self(),T]).
  312. kicker(Who,T) ->
  313. sleep_or_stop(T),
  314. Who ! {self(),kick},
  315. kicker(Who,T).
  316. stop_kicker(Kicker) -> Kicker ! stop.
  317. sleep_or_stop(T) ->
  318. receive
  319. stop -> exit(stopped)
  320. after T -> true
  321. end.