Răsfoiți Sursa

Better fx curve editor (#178)

* Shift+drag to move selection only in value (lock time)

Was possible to do on a single key but not on a square selection.

* alt key to lock value on selection

* alt key to lock value on single key

* Duplicate keys feature

* Trigger duplicate on key press instead of on move
Jed974 4 ani în urmă
părinte
comite
bf3b703272
6 a modificat fișierele cu 130 adăugiri și 11 ștergeri
  1. 3 0
      bin/style.css
  2. 4 0
      bin/style.less
  3. 42 2
      hide/comp/CurveEditor.hx
  4. 71 8
      hide/view/FXEditor.hx
  5. 9 0
      hrt/prefab/Curve.hx
  6. 1 1
      hrt/prefab/fx/Emitter.hx

+ 3 - 0
bin/style.css

@@ -869,6 +869,9 @@ input[type=checkbox]:checked:after {
 .hide-curve-editor svg .graph .handles rect:hover.selected {
   fill: #ffffff;
 }
+.hide-curve-editor svg .graph .handles rect.preview {
+  fill: #c06f06;
+}
 .hide-curve-editor svg .selection-overlay rect {
   mix-blend-mode: multiply;
   fill: #888;

+ 4 - 0
bin/style.less

@@ -904,6 +904,7 @@ input[type=checkbox] {
 	}
 	svg {
 		@selectCol: rgb(255, 255, 255);
+		@previewCol: rgb(192, 111, 6);
 		@lineCol: rgb(255, 31, 31);
 		border-right: 1px solid black;
 
@@ -969,6 +970,9 @@ input[type=checkbox] {
 				circle.selected, circle:hover.selected, rect.selected, rect:hover.selected {
 					fill: @selectCol;
 				}
+				rect.preview {
+					fill: @previewCol;
+				}
 			}
 		}
 

+ 42 - 2
hide/comp/CurveEditor.hx

@@ -30,6 +30,7 @@ class CurveEditor extends Component {
 	var lastValue : Dynamic;
 
 	var selectedKeys: Array<CurveKey> = [];
+	var previewKeys: Array<CurveKey> = [];
 
 	public function new(undo, ?parent) {
 		super(parent,null);
@@ -156,6 +157,14 @@ class CurveEditor extends Component {
 		afterChange();
 	}
 
+	function addPreviewKey(time: Float, ?val: Float) {
+		beforeChange();
+		if(minValue < maxValue)
+			val = hxd.Math.clamp(val, minValue, maxValue);
+		curve.addPreviewKey(time, val);
+		afterChange();
+	}
+
 	function fixKey(key : CurveKey) {
 		var index = curve.keys.indexOf(key);
 		var prev = curve.keys[index-1];
@@ -543,6 +552,12 @@ class CurveEditor extends Component {
 			return popup;
 		}
 
+		for(key in curve.previewKeys) {
+			var kx = xScale*(key.time);
+			var ky = -yScale*(key.value);
+			var keyHandle = addRect(keyHandles, kx, ky);
+			keyHandle.addClass("preview");
+		}
 		for(key in curve.keys) {
 			var kx = xScale*(key.time);
 			var ky = -yScale*(key.value);
@@ -558,6 +573,7 @@ class CurveEditor extends Component {
 					var offset = element.offset();
 					beforeChange();
 					var startT = key.time;
+					var startV = key.value;
 
 					startDrag(function(e) {
 						var lx = e.clientX - offset.left;
@@ -574,6 +590,8 @@ class CurveEditor extends Component {
 						}
 						if(lockKeyX || e.shiftKey)
 							key.time = startT;
+						if(e.altKey)
+							key.value = startV;
 						fixKey(key);
 						refreshGraph(true, key);
 						onKeyMove(key, prevTime, prevVal);
@@ -663,17 +681,39 @@ class CurveEditor extends Component {
 					if(e.which != 1) return;
 					e.preventDefault();
 					e.stopPropagation();
+					var deltaX = 0;
+					var deltaY = 0;
 					var lastX = e.clientX;
 					var lastY = e.clientY;
 					startDrag(function(e) {
 						var dx = e.clientX - lastX;
 						var dy = e.clientY - lastY;
+						if(lockKeyX || e.shiftKey)
+							dx = 0;
+						if(e.altKey)
+							dy = 0;
 						for(key in selectedKeys) {
 							key.time += dx / xScale;
+							if(lockKeyX || e.shiftKey)
+								key.time -= deltaX / xScale;
 							key.value -= dy / yScale;
+							if(e.altKey)
+								key.value += deltaY / yScale;
+						}
+						deltaX += dx;
+						deltaY += dy;
+						if(lockKeyX || e.shiftKey) {
+							lastX -= deltaX;
+							deltaX = 0;
+						}
+						else
+							lastX = e.clientX;
+						if(e.altKey) {
+							lastY -= deltaY;
+							deltaY = 0;
 						}
-						lastX = e.clientX;
-						lastY = e.clientY;
+						else
+							lastY = e.clientY;
 						refreshGraph(true);
 						onChange(true);
 					}, function(e) {

+ 71 - 8
hide/view/FXEditor.hx

@@ -678,6 +678,8 @@ class FXEditor extends FileView {
 				refreshViews();
 			}
 
+			var duplicateMode = false;
+			var previewKeys = [];
 			function setupSelectDrag(element: js.jquery.JQuery, update: Float->Float->Void) {
 				element.mousedown(function(e) {
 					updateSelected();
@@ -699,7 +701,48 @@ class FXEditor extends FileView {
 						updateSelectPos();
 						lastTime = time;
 					}, function(e) {
+						for (pKey in previewKeys) {
+							var curve = curves.find((curve) -> return curve.previewKeys.contains(pKey));
+							curve.previewKeys.remove(pKey);
+						}
+						previewKeys = [];
+						for(ce in curveEdits) {
+							ce.refreshGraph(true);
+							ce.onChange(true);
+						}
 						afterChange();
+					}, function(e) {
+						if (e.keyCode == hxd.Key.ALT){
+							if (!duplicateMode) {
+								duplicateMode = !duplicateMode;
+								for (key in allKeys) {
+									var curve = curves.find((curve) -> return curve.keys.contains(key));
+									var pKey = curve.addPreviewKey(key.time, key.value);
+									previewKeys.push(pKey);
+								}
+								allKeys = [];
+								for(ce in curveEdits) {
+									ce.refreshGraph(true);
+									ce.onChange(true);
+								}
+							}
+						}
+					}, function(e) {
+						if (e.keyCode == hxd.Key.ALT){
+							if (duplicateMode) {
+								duplicateMode = !duplicateMode;
+								for (pKey in previewKeys) {
+									var curve = curves.find((curve) -> return curve.previewKeys.contains(pKey));
+									curve.previewKeys.remove(pKey);
+									allKeys.push(curve.addKey(pKey.time, pKey.value));
+								}
+								previewKeys = [];
+								for(ce in curveEdits) {
+									ce.refreshGraph(true);
+									ce.onChange(true);
+								}
+							}
+						}
 					});
 				});
 			}
@@ -709,8 +752,14 @@ class FXEditor extends FileView {
 				if(selectMax > selectMin + 0.1) {
 					var scaleFactor = (selectMax + shift - selectMin) / (selectMax - selectMin);
 
-					for(key in allKeys)
-						key.time = (key.time - selectMin) * scaleFactor + selectMin;
+					if (duplicateMode) {
+						for (key in previewKeys)
+							key.time = (key.time - selectMin) * scaleFactor + selectMin;
+					}
+					else {
+						for(key in allKeys)
+							key.time = (key.time - selectMin) * scaleFactor + selectMin;
+					}
 
 					selectMax += shift;
 				}
@@ -721,8 +770,14 @@ class FXEditor extends FileView {
 				if(selectMax > selectMin + 0.1) {
 					var scaleFactor = (selectMax - (selectMin + shift)) / (selectMax - selectMin);
 
-					for(key in allKeys)
-						key.time = selectMax - (selectMax - key.time) * scaleFactor;
+					if (duplicateMode) {
+						for(key in previewKeys)
+							key.time = selectMax - (selectMax - key.time) * scaleFactor;
+					}
+					else {
+						for(key in allKeys)
+							key.time = selectMax - (selectMax - key.time) * scaleFactor;
+					}
 
 					selectMin += shift;
 				}
@@ -731,11 +786,17 @@ class FXEditor extends FileView {
 			setupSelectDrag(select, function(time, lastTime) {
 				var shift = time - lastTime;
 
-				for(key in allKeys)
-					key.time += shift;
-
+				if (duplicateMode) {
+					for(key in previewKeys)
+						key.time += shift;
+				}
+				else {
+					for(key in allKeys)
+						key.time += shift;
+				}
 				selectMin += shift;
 				selectMax += shift;
+
 			});
 		}
 
@@ -1184,11 +1245,13 @@ class FXEditor extends FileView {
 		}
 	}
 
-	function startDrag(onMove: js.jquery.Event->Void, onStop: js.jquery.Event->Void) {
+	function startDrag(onMove: js.jquery.Event->Void, onStop: js.jquery.Event->Void, ?onKeyDown: js.jquery.Event->Void, ?onKeyUp: js.jquery.Event->Void) {
 		var el = new Element(element[0].ownerDocument.body);
 		var startX = null, startY = null;
 		var dragging = false;
 		var threshold = 3;
+		el.keydown(onKeyDown);
+		el.keyup(onKeyUp);
 		el.on("mousemove.fxedit", function(e: js.jquery.Event) {
 			if(startX == null) {
 				startX = e.clientX;

+ 9 - 0
hrt/prefab/Curve.hx

@@ -32,6 +32,7 @@ class Curve extends Prefab {
 
 	@:s public var keyMode : CurveKeyMode = Linear;
 	@:c public var keys : CurveKeys = [];
+	@:c public var previewKeys : CurveKeys = [];
 
 	@:s public var loop : Bool = false;
 
@@ -122,6 +123,14 @@ class Curve extends Prefab {
 		return key;
 	}
 
+	public function addPreviewKey(time: Float, val: Float) {
+		var key = new hrt.prefab.Curve.CurveKey();
+		key.time = time;
+		key.value = val;
+		previewKeys.push(key);
+		return key;
+	}
+
 	public function getBounds() {
 		// TODO: Take bezier handles into account
 		var ret = new h2d.col.Bounds();

+ 1 - 1
hrt/prefab/fx/Emitter.hx

@@ -1244,7 +1244,7 @@ class Emitter extends Object3D {
 					else if(v.match(VVector(VOne, VOne, VOne)))
 						v = VOne;
 					return v;
-					
+
 				default:
 					return makeCompVal(baseProp, param.def != null ? param.def : 0.0, randProp, param.name, "");
 			}