indexing description: "3D Component for the elfractal example" author: "Lars Krapf" date: "$Date$" revision: "$Revision$" class EF_3D_DISPLAY inherit EM_3D_COMPONENT DOUBLE_MATH export {NONE} all end create make_from_dimension feature {NONE} -- Initialisation make_from_dimension(a_width: INTEGER; a_height: INTEGER) is -- Initialise default values. require valid_dimension: a_width > 0 and a_height > 0 do make_3d_component set_max_view_distance(400.0) set_dimension(a_width, a_height) set_tooltip ("left-drag to rotate, right-drag to zoom") longitude := 10 latitude := 20 distance := 100 angle := 0.6 create l_system.make_empty -- Prepare Usercommands create user_commands_string.make_empty create user_commands.make (19) -- Event registration mouse_dragged_event.subscribe (agent handle_mouse_dragged) mouse_wheel_up_event.subscribe (agent handle_mouse_wheel_up) mouse_wheel_down_event.subscribe (agent handle_mouse_wheel_down) ensure system_exists: l_system /= Void end feature -- Element Change set_user_commands_string(a_string: STRING) is -- Set the String representing the user definitions for the l-system require user_commands_valid: a_string /= Void do user_commands_string := a_string parse_user_commands ensure user_commands_set: user_commands_string /= Void end set_system_string(a_string: STRING) is -- Set the String representing the system to execute require a_string_not_void: a_string /= Void do l_system := a_string ensure system_valid: l_system /= Void end set_iterations(n: INTEGER) is -- Set the number of iterations require atleast_one_iteration: n >= 0 do iterations := n ensure iterations_valid: iterations >= 0 end set_simple_mode(mode: BOOLEAN) is -- Set simple mode do simple_mode := mode end feature -- Access longitude: REAL -- Longitude of the eye (in degree) around center (0,0,0) latitude: REAL -- Latitude of the eye (in degree) around center (0,0,0) distance: REAL -- Distance of the eye to center (0,0,0) feature -- Basic operations create_script is -- Create the GL_Script and redraw local state: EF_STATE do gl_new_list(script, EM_GL_COMPILE) state := draw_lines(l_system, create {EF_STATE}.make,iterations) gl_end_list end set_animated(status: BOOLEAN) is -- set animated status do animated := status end feature -- Drawing draw is -- Draw the compiled GLList do if animated then angle := angle + 0.005 create_script end -- Set up eye position gl_translatef (0, 0, -distance) gl_rotatef (-longitude, 0, 1, 0) gl_rotatef (latitude, cosine (-longitude / 180 * pi),0,sine (-longitude / 180 * pi)) -- Draw List gl_begin(EM_GL_LINES) gl_call_list(script) gl_end end feature {NONE} -- Mouse management handle_mouse_dragged (event: EM_MOUSEMOTION_EVENT) is -- Handle mouse dragged event. do if event.button_state_left then longitude := longitude - event.motion.x latitude := latitude + event.motion.y if latitude > 90 then latitude := 90 elseif latitude < -90 then latitude := -90 end end if event.button_state_right then distance := distance + event.motion.y if distance < 0 then distance := 0 end if distance > 300 then distance := 300 end end end handle_mouse_wheel_up is -- Handle mouse wheel up event. do angle := angle + 0.02 create_script end handle_mouse_wheel_down is -- Handle mouse wheel down event. do angle := angle - 0.02 create_script end feature {NONE} -- Implementation parse_user_commands is -- Go to user definitions and fill hash_table local lines: LIST [STRING] line: LIST [STRING] do user_commands.clear_all -- go through user_command_string line by line lines := user_commands_string.split('%N') from lines.start until lines.after loop -- split definition on '=' and store it into hashtable line := lines.item.split('=') if line.count = 2 then user_commands.put (line.i_th(2), line.i_th(1)) end lines.forth end end is_builtin(a_char: CHARACTER):BOOLEAN is -- check if a_char is a special character do Result := a_char.is_equal('[') or a_char.is_equal(']') end draw_lines(system: STRING; a_state: EF_STATE; iteration: INTEGER):EF_STATE is local i,j: INTEGER state, old_state: EF_STATE token: STRING do state := a_state.twin -- Go through Systemstring, char by char and parse tokens from i := 2 j := 1 until j > system.count loop -- found end of current token? if i > system.count or system.item(i).is_upper or is_builtin(system.item(i)) then token := system.substring(j,i-1) j := i -- token recognized? if user_commands.has(token) then if iteration > 0 then -- execute user defined command state := draw_lines(user_commands[token], state, iteration-1) else if simple_mode then -- use colors based on current coordinates gl_color3f(1-state.x*0.02,1-state.y*0.02,1-state.z*0.02) else -- use colors from state gl_color3f(state.r, state.g, state.b) end -- draw a line in current direction gl_vertex3f(state.x, state.y, state.z) state.set_coordinates(state.x-sine(state.phi)*cosine(state.rho),state.y+sine(state.rho),state.z+cosine(state.rho)*cosine(state.phi)) gl_vertex3f(state.x, state.y, state.z) end end -- color Builtins if token.is_equal("Red+") then state.set_colors(state.r + 0.1, state.g, state.b) end if token.is_equal("Red-") then state.set_colors(state.r - 0.1, state.g, state.b) end if token.is_equal("Green+") then state.set_colors(state.r, state.g + 0.1, state.b) end if token.is_equal("Green-") then state.set_colors(state.r, state.g - 0.1, state.b) end if token.is_equal("Blue+") then state.set_colors(state.r, state.g, state.b + 0.1) end if token.is_equal("Blue-") then state.set_colors(state.r, state.g, state.b - 0.1) end -- step forward if token.is_equal("F") then if iteration = 0 then -- if no more iterations left if simple_mode then -- use colors based on current coordinates gl_color3f(1-state.x*0.02,1-state.y*0.02,1-state.z*0.02) else -- use colors from state gl_color3f(state.r, state.g, state.b) end -- draw a line in current direction gl_vertex3f(state.x, state.y, state.z) state.set_coordinates(state.x-sine(state.phi)*cosine(state.rho),state.y+sine(state.rho),state.z+cosine(state.rho)*cosine(state.phi)) gl_vertex3f(state.x, state.y, state.z) else -- otherwise reiterate into current system state := draw_lines(l_system, state, iteration-1) end end -- directional builtins if token.is_equal("D") then state.set_angles(state.phi+angle, state.rho) end if token.is_equal("U") then state.set_angles(state.phi-angle, state.rho) end if token.is_equal("R") then state.set_angles(state.phi, state.rho+angle) end if token.is_equal("L") then state.set_angles(state.phi, state.rho-angle) end -- special builtins if token.is_equal("[") then -- store current state old_state := state.twin end if token.is_equal("]") then -- restore state if old_state /= Void then state := old_state.twin end end end i := i + 1 end Result := state end iterations: INTEGER -- the number of iterations angle: DOUBLE -- the default angle of commands U,D,L and R script: INTEGER is 1 -- the opengl list to use l_system: STRING -- a string representation of the l-type system user_commands_string: STRING -- string representation of the user commands user_commands: HASH_TABLE [STRING, STRING] -- a hashtable containing user-defined commands animated: BOOLEAN -- animate angles? simple_mode: BOOLEAN -- simple mode? end