{ function GravityUI() { // Read and evaluate all the files that contain libraries of methods used by this script. this.includeScripts = [ "lib/vectorMath.jsx", "lib/bouncingBallSimulation.jsx" ]; for (var i = 0; i < this.includeScripts.length; i++) { var scriptFile = new File(this.includeScripts[i]); scriptFile.open(); eval(scriptFile.read()); scriptFile.close(); } function on_run_click() { if (app.project == null) { alert("Please open a project, establish an active comp, select at least one layer, and run again."); return; } var activeItem = app.project.activeItem; if (activeItem == null || !(activeItem instanceof CompItem)) { alert("Please establish an active comp, select at least one layer, and run again."); return; } var activeComp = activeItem; var selectedLayers = activeComp.selectedLayers; if (selectedLayers == null || selectedLayers.length < 1) { alert("Please select at least one layer and run again."); return; } for (var i = 0; i < selectedLayers.length; i++) { gravityUI.bouncingBallSimulation.setBallLayer(selectedLayers[i]); gravityUI.bouncingBallSimulation.startTime = activeComp.workAreaStart; gravityUI.bouncingBallSimulation.endTime = activeComp.workAreaStart + activeComp.workAreaDuration; gravityUI.bouncingBallSimulation.frameRate = activeComp.frameRate; gravityUI.bouncingBallSimulation.frameDuration = activeComp.frameDuration; if (gravityUI.comp_bounds_radio.value == true) { gravityUI.bouncingBallSimulation.setBounds(0,activeComp.width,0,activeComp.height); } else { with(gravityUI) { bouncingBallSimulation.setBounds(custom_left, custom_right, custom_top, custom_bottom); } } gravityUI.bouncingBallSimulation.runSimulation(); gravityUI.bouncingBallSimulation.setBallLayer(null); } } function on_help_click() { // Too much help to show in one alert! // Show it in 3 successive windows var view_more = confirm("Gravity Demo\n\n" + "This demo runs a 2D simulation of objects bouncing inside a " + "box-shaped enclosure. The simulation is performed on each " + "selected layer in the active comp.\n\n" + "Run Simulation:\n" + " Press this button to generate keyframes for each layer, " + "based on the current parameter settings.\n\n" + "View more help?"); if (view_more) { view_more = confirm("Parameters controlled with this UI:\n\n" + "Bounds:\n" + " The bounds specify walls against which the objects will bounce. " + "Choose \"Comp Bounds\" to bounce against the edges of the active comp. " + "Choose \"Custom Bounds\" and you can edit the left, right, top, and bottom " + "bounds of the enclosure.\n\n" + "Drag:\n" + " Corresponds to air resistance. A value of 0 means no air resistance. " + "Higher values will exert a force that acts opposite the direction of travel.\n\n" + "Elasticity:\n" + " Controls the amount of energy retained when the object bounces against a wall. " + "A value of 0 means all energy is lost and the object splats to a halt. " + "A value of 1 means all energy is retained, and the object bounces off at full speed.\n\n" + "Gravity:\n" + " Gravity always acts in a downward direction. Higher values mean stronger gravity.\n\n" + "Density:\n"+ " Higher values mean denser objects. Denser objects are less affected by drag. " + "Mass is calculated as density times width times height. " + "Width and height are determined implicitly (see next screen).\n\n" + "View more help?"); } if (view_more) { alert("Parameters determined by the active comp or the layer:\n\n"+ "Start Time and End Time:\n"+ " Keyframes are only generated between the start time and end time. " + "These times will match the work area of the active comp.\n\n" + "Frame Rate:\n"+ " Keyframes are generated with a frequency that matches the frame rate of the active comp.\n\n" + "Height and Width:\n"+ " Height and width determine the edges of the object when it bounces off a wall. " + "They also determine the mass of the object, calculated as area times density. " + "Height and width are calculated independently for each selected layer. " + "The width is the layer width multiplied by x-scale. "+ "The height is the layer height multiplied by y-scale.\n\n"+ "Start Position:\n"+ " Before the simulation is run, each layer's position at the start time is recorded. " + "This becomes the start position for the simulation.\n\n" + "Start Velocity:\n"+ " Before the simulation is run, each layer's velocity at the start time is recorded. " + "This becomes the start velocity for the simulation."); } } function on_comp_bounds_click() { gravityUI.left_edittext.hide(); gravityUI.right_edittext.hide(); gravityUI.top_edittext.hide(); gravityUI.bottom_edittext.hide(); gravityUI.left_displaytext.show(); gravityUI.right_displaytext.show(); gravityUI.top_displaytext.show(); gravityUI.bottom_displaytext.show(); gravityUI.updateBoundsDisplayTextFromActiveComp(); } function on_custom_bounds_click() { gravityUI.left_edittext.show(); gravityUI.right_edittext.show(); gravityUI.top_edittext.show(); gravityUI.bottom_edittext.show(); gravityUI.left_displaytext.hide(); gravityUI.right_displaytext.hide(); gravityUI.top_displaytext.hide(); gravityUI.bottom_displaytext.hide(); } function on_custom_boundstext_click() { with(gravityUI) { var parsedValue = parseFloat(this.text); var parsesFully = !isNaN(parsedValue) && (parsedValue == this.text); // If the entire new value does not parse to a number, restore the saved value and return. // if (!parsesFully) { if (this == left_edittext) { this.text = custom_left; } if (this == right_edittext) { this.text = custom_right; } if (this == top_edittext) { this.text = custom_top; } if (this == bottom_edittext) { this.text = custom_bottom; } this.notify(); return; } // save the changed value if (this == left_edittext) { custom_left = parsedValue; } if (this == right_edittext) { custom_right = parsedValue; } if (this == top_edittext) { custom_top = parsedValue; } if (this == bottom_edittext) { custom_bottom = parsedValue; } // make sure left < right and top < bottom if (custom_left >= custom_right) { if (this == left_edittext) { right_edittext.text = custom_left + 1; right_edittext.notify(); } else { left_edittext.text = custom_right - 1; left_edittext.notify(); } } if (custom_top >= custom_bottom) { if (this == top_edittext) { bottom_edittext.text = custom_top + 1; bottom_edittext.notify(); } else { top_edittext.text = custom_bottom - 1; top_edittext.notify(); } } } } function on_drag_slider_changed() { // drag is stored in slider as [1..100], and in // edittext&simulation as [0..10] with(gravityUI) { if (Math.abs(bouncingBallSimulation.drag * 10 - this.value)>0.5) { bouncingBallSimulation.drag = 0.1 * this.value; drag_edittext.text = bouncingBallSimulation.drag.toString(); drag_edittext.notify(); } } } function on_drag_text_changed() { // drag is stored in slider as [1..100], and in // edittext&simulation as [0..10] with(gravityUI) { if (bouncingBallSimulation.drag != this.text) { bouncingBallSimulation.drag = this.text; drag_slider.value = this.text * 10.0; drag_slider.notify(); } } } function on_elasticity_slider_changed() { // elasticity is stored in slider as [1..100], and in // edittext&simulation as [0..1] with(gravityUI) { if (Math.abs(bouncingBallSimulation.elasticity * 100 - this.value)>0.5) { bouncingBallSimulation.elasticity = 0.01 * this.value; elasticity_edittext.text = bouncingBallSimulation.elasticity.toString(); elasticity_edittext.notify(); } } } function on_elasticity_text_changed() { // elasticity is stored in slider as [1..100], and in // edittext&simulation as [0..1] with(gravityUI) { if (bouncingBallSimulation.elasticity != this.text) { bouncingBallSimulation.elasticity = this.text; elasticity_slider.value = this.text * 100.0; elasticity_slider.notify(); } } } function on_gravity_slider_changed() { // gravity is stored in slider as [1..5000], which maps to // edittext&simulation as [1..50] with(gravityUI) { if (Math.abs(bouncingBallSimulation.gravity[1] * 10 - this.value) > 0.5) { bouncingBallSimulation.gravity = [0,this.value * 0.1,0]; gravity_edittext.text = (this.value * 0.1).toString(); gravity_edittext.notify(); } } } function on_gravity_text_changed() { with(gravityUI) { if ((bouncingBallSimulation.gravity[1]).toString() != this.text){ bouncingBallSimulation.gravity = [0,this.text,0];//[0,this.text.toFloat(),0]; gravity_slider.value = this.text * 10;//.toFloat(); gravity_slider.notify(); } } } function on_density_slider_changed() { // density is stored in slider as [1..100], which maps to // edittext&simulation as [1..100] with(gravityUI) { if (Math.abs(bouncingBallSimulation.density - this.value) > 0.5) { bouncingBallSimulation.density = this.value; density_edittext.text = this.value.toString(); density_edittext.notify(); } } } function on_density_text_changed() { with(gravityUI) { if ((bouncingBallSimulation.density).toString() != this.text){ bouncingBallSimulation.density = this.text; density_slider.value = this.text; density_slider.notify(); } } } function my_updateBoundsDisplayTextFromActiveComp() { if (app.project != null && app.project.activeItem != null && app.project.activeItem instanceof CompItem) { var activeComp = app.project.activeItem; this.left_displaytext.text = 0; this.right_displaytext.text = activeComp.width; this.top_displaytext.text = 0; this.bottom_displaytext.text = activeComp.height; this.left_displaytext.notify(); this.right_displaytext.notify(); this.top_displaytext.notify(); this.bottom_displaytext.notify(); } } function my_updateBoundsEditTextFromActiveComp() { if (app.project != null && app.project.activeItem != null && app.project.activeItem instanceof CompItem) { var activeComp = app.project.activeItem; this.custom_left = 0; this.custom_right = activeComp.width; this.custom_top = 0; this.custom_bottom = activeComp.height; with(this) { left_edittext.text = custom_left; right_edittext.text = custom_right; top_edittext.text = custom_top; bottom_edittext.text = custom_bottom; left_edittext.notify(); right_edittext.notify(); top_edittext.notify(); bottom_edittext.notify(); } } } function my_initUI() { with (this) { drag_edittext.text = bouncingBallSimulation.drag; drag_edittext.notify(); drag_slider.value = bouncingBallSimulation.drag*10; drag_slider.notify(); elasticity_edittext.text = bouncingBallSimulation.elasticity; elasticity_edittext.notify(); elasticity_slider.value = bouncingBallSimulation.elasticity*100; elasticity_slider.notify(); gravity_edittext.text = bouncingBallSimulation.gravity[1]; gravity_edittext.notify(); gravity_slider.value = bouncingBallSimulation.gravity[1]*10; gravity_slider.notify(); density_edittext.text = bouncingBallSimulation.density; density_edittext.notify(); density_slider.value = bouncingBallSimulation.density; density_slider.notify(); updateBoundsDisplayTextFromActiveComp(); updateBoundsEditTextFromActiveComp(); } } // Define member-type methods as variables // this.initUI = my_initUI; this.updateBoundsDisplayTextFromActiveComp = my_updateBoundsDisplayTextFromActiveComp; this.updateBoundsEditTextFromActiveComp = my_updateBoundsEditTextFromActiveComp; // Create the bouncing ball simulation this.bouncingBallSimulation = new BouncingBallSimulation(); // Store the default values for custom bouncing bounds this.custom_left = 0; this.custom_right = 100; this.custom_top = 0; this.custom_bottom = 100; // Create and show a floating palette // this.my_palette = new Window("palette","Bouncing Ball"); this.my_palette.bounds = [300,100,570,400]; // "Run" Button this.run_button = this.my_palette.add("button", [10,10,150,30], "Run Simulation"); // "Help" Button this.help_button = this.my_palette.add("button", [160,10,255,30], "?"); // "Bounds" Panel this.bounds_panel = this.my_palette.add("panel", [10,35,260,145], "Bounds"); this.comp_bounds_radio = this.bounds_panel.add("radiobutton", [10, 15, 125, 40], "Comp Bounds"); this.custom_bounds_radio = this.bounds_panel.add("radiobutton", [130, 15, 240, 40], "Custom Bounds"); this.comp_bounds_radio.value = true; this.bounds_panel.add("statictext", [ 10, 47, 55, 65], "Left:"); this.bounds_panel.add("statictext", [100, 47, 145, 65], "Right:"); this.bounds_panel.add("statictext", [ 10, 72, 55, 90], "Top:"); this.bounds_panel.add("statictext", [100, 72, 145, 90], "Bottom:"); this.left_displaytext = this.bounds_panel.add("edittext", [ 60, 45, 95, 65], this.custom_left, {readonly:true}); this.right_displaytext = this.bounds_panel.add("edittext", [155, 45, 190, 65], this.custom_right, {readonly:true}); this.top_displaytext = this.bounds_panel.add("edittext", [ 60, 70, 95, 90], this.custom_top, {readonly:true}); this.bottom_displaytext = this.bounds_panel.add("edittext", [155, 70, 190, 90], this.custom_bottom, {readonly:true}); this.left_edittext = this.bounds_panel.add("edittext", [ 60, 45, 95, 65], this.custom_left); this.right_edittext = this.bounds_panel.add("edittext", [155, 45, 190, 65], this.custom_right); this.top_edittext = this.bounds_panel.add("edittext", [ 60, 70, 95, 90], this.custom_top); this.bottom_edittext = this.bounds_panel.add("edittext", [155, 70, 190, 90], this.custom_bottom); this.left_edittext.hide(); this.right_edittext.hide(); this.top_edittext.hide(); this.bottom_edittext.hide(); // "Controls" Panel var labelRight = 75; var sliderLeft = 130; var sliderRight = 233; var editLeft = 80; var editRight = 120; this.control_panel = this.my_palette.add("panel", [10,155,260,290], "Controls"); this.control_panel.add("statictext", [10, 22, labelRight, 40], "Drag:"); this.drag_edittext = this.control_panel.add("edittext", [editLeft, 20, editRight, 40], "0.0"); this.drag_slider = this.control_panel .add("slider", [sliderLeft, 20, sliderRight, 40], this.bouncingBallSimulation.drag*10, 0, 100); this.control_panel.add("statictext", [10, 47, labelRight, 65], "Elasticity:"); this.elasticity_edittext = this.control_panel.add("edittext", [editLeft, 45, editRight, 65], "1.0"); this.elasticity_slider = this.control_panel.add("slider", [sliderLeft, 45, sliderRight, 65], this.bouncingBallSimulation.elasticity*100, 0, 100); this.control_panel.add("statictext", [10, 72, labelRight, 90], "Gravity:"); this.gravity_edittext = this.control_panel.add("edittext", [editLeft, 70, editRight, 90], "98.0"); this.gravity_slider = this.control_panel.add("slider", [sliderLeft, 70, sliderRight, 90], this.bouncingBallSimulation.gravity*10, 0.0, 5000.0); this.control_panel.add("statictext", [10, 97, labelRight, 115], "Density:"); this.density_edittext = this.control_panel.add("edittext", [editLeft, 95, editRight, 115], "10"); this.density_slider = this.control_panel.add("slider", [sliderLeft, 95, sliderRight, 115], this.bouncingBallSimulation.density, 1, 100); // callbacks this.run_button.onClick = on_run_click; this.help_button.onClick = on_help_click; // this.comp_bounds_radio.onClick = on_comp_bounds_click; this.custom_bounds_radio.onClick = on_custom_bounds_click; // this.left_edittext.onChange = on_custom_boundstext_click; this.right_edittext.onChange = on_custom_boundstext_click; this.top_edittext.onChange = on_custom_boundstext_click; this.bottom_edittext.onChange = on_custom_boundstext_click; // this.drag_slider.onChange = on_drag_slider_changed; this.drag_edittext.onChange = on_drag_text_changed; this.elasticity_slider.onChange = on_elasticity_slider_changed; this.elasticity_edittext.onChange = on_elasticity_text_changed; this.gravity_slider.onChange = on_gravity_slider_changed; this.gravity_edittext.onChange = on_gravity_text_changed; this.density_slider.onChange = on_density_slider_changed; this.density_edittext.onChange = on_density_text_changed; this.my_palette.show(); } var gravityUI = new GravityUI(); gravityUI.initUI(); }