123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- package hrt.prefab;
- using Lambda;
- class CurveHandle {
- public var dt: Float;
- public var dv: Float;
- public function new(t, v) {
- this.dt = t;
- this.dv = v;
- }
- }
- enum abstract CurveKeyMode(Int) {
- var Aligned = 0;
- var Free = 1;
- var Linear = 2;
- var Constant = 3;
- }
- class CurveKey {
- public var time: Float;
- public var value: Float;
- public var mode: CurveKeyMode;
- public var prevHandle: CurveHandle;
- public var nextHandle: CurveHandle;
- public function new() {}
- }
- typedef CurveKeys = Array<CurveKey>;
- 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;
- public var maxTime : Float;
- public var duration(get, never): Float;
- function get_duration() {
- if(keys.length == 0) return 0.0;
- return keys[keys.length-1].time;
- }
- public function new(?parent) {
- super(parent);
- this.type = "curve";
- }
- public override function load(o:Dynamic) {
- super.load(o);
- keys = [];
- if(o.keys != null) {
- for(k in (o.keys: Array<Dynamic>)) {
- var nk = new CurveKey();
- nk.time = k.time;
- nk.value = k.value;
- nk.mode = k.mode;
- if(k.prevHandle != null)
- nk.prevHandle = new CurveHandle(k.prevHandle.dt, k.prevHandle.dv);
- if(k.nextHandle != null)
- nk.nextHandle = new CurveHandle(k.nextHandle.dt, k.nextHandle.dv);
- keys.push(nk);
- }
- }
- if( keys.length == 0 ) {
- addKey(0.0, 0.0);
- addKey(1.0, 1.0);
- }
- }
- public override function save() {
- var obj : Dynamic = super.save();
- var keysDat = [];
- for(k in keys) {
- var o = {
- time: k.time,
- value: k.value,
- mode: k.mode
- };
- if(k.prevHandle != null) Reflect.setField(o, "prevHandle", { dv: k.prevHandle.dv, dt: k.prevHandle.dt });
- if(k.nextHandle != null) Reflect.setField(o, "nextHandle", { dv: k.nextHandle.dv, dt: k.nextHandle.dt });
- keysDat.push(o);
- }
- obj.keys = keysDat;
- return obj;
- }
- static inline function bezier(c0: Float, c1:Float, c2:Float, c3: Float, t:Float) {
- var u = 1 - t;
- return u * u * u * c0 + c1 * 3 * t * u * u + c2 * 3 * t * t * u + t * t * t * c3;
- }
- public function findKey(time: Float, tolerance: Float) {
- var minDist = tolerance;
- var closest = null;
- for(k in keys) {
- var d = hxd.Math.abs(k.time - time);
- if(d < minDist) {
- minDist = d;
- closest = k;
- }
- }
- return closest;
- }
- public function addKey(time: Float, ?val: Float, ?mode=null) {
- var index = 0;
- for(ik in 0...keys.length) {
- var key = keys[ik];
- if(time > key.time)
- index = ik + 1;
- }
- if(val == null)
- val = getVal(time);
- var key = new hrt.prefab.Curve.CurveKey();
- key.time = time;
- key.value = val;
- key.mode = mode != null ? mode : (keys[index] != null ? keys[index].mode : keyMode);
- keys.insert(index, key);
- 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();
- for(k in keys) {
- ret.addPos(k.time, k.value);
- }
- return ret;
- }
- public function getVal(time: Float) : Float {
- switch(keys.length) {
- case 0: return 0;
- case 1: return keys[0].value;
- default:
- }
- if (loop)
- time = time % keys[keys.length-1].time;
- var idx = -1;
- for(ik in 0...keys.length) {
- var key = keys[ik];
- if(time > key.time)
- idx = ik;
- }
- if(idx < 0)
- return keys[0].value;
- var cur = keys[idx];
- var next = keys[idx + 1];
- if(next == null || cur.mode == Constant)
- return cur.value;
- var minT = 0.;
- var maxT = 1.;
- var maxDelta = 1./ 25.;
- inline function sampleTime(t) {
- return bezier(
- cur.time,
- cur.time + (cur.nextHandle != null ? cur.nextHandle.dt : 0.),
- next.time + (next.prevHandle != null ? next.prevHandle.dt : 0.),
- next.time, t);
- }
- inline function sampleVal(t) {
- return bezier(
- cur.value,
- cur.value + (cur.nextHandle != null ? cur.nextHandle.dv : 0.),
- next.value + (next.prevHandle != null ? next.prevHandle.dv : 0.),
- next.value, t);
- }
- while( maxT - minT > maxDelta ) {
- var t = (maxT + minT) * 0.5;
- var x = sampleTime(t);
- if( x > time )
- maxT = t;
- else
- minT = t;
- }
- var x0 = sampleTime(minT);
- var x1 = sampleTime(maxT);
- var dx = x1 - x0;
- var xfactor = dx == 0 ? 0.5 : (time - x0) / dx;
- var y0 = sampleVal(minT);
- var y1 = sampleVal(maxT);
- var y = y0 + (y1 - y0) * xfactor;
- return y;
- }
- public function getSum(time: Float) : Float {
- var duration = keys[keys.length-1].time;
- if(loop && time > duration) {
- var cycles = Math.floor(time / duration);
- return getSum(duration) * cycles + getSum(time - cycles);
- }
- var sum = 0.0;
- for(ik in 0...keys.length) {
- var key = keys[ik];
- if(time < key.time)
- break;
- if(ik == 0 && key.time > 0) {
- // Account for start of curve
- sum += key.time * key.value;
- }
- var nkey = keys[ik + 1];
- if(nkey != null) {
- if(time > nkey.time) {
- // Full interval
- sum += key.value * (nkey.time - key.time);
- if(key.mode != Constant)
- sum += 0.5 * (nkey.time - key.time) * (nkey.value - key.value);
- }
- else {
- // Split interval
- sum += key.value * (time - key.time);
- if(key.mode != Constant)
- sum += 0.5 * (time - key.time) * hxd.Math.lerp(key.value, nkey.value, (time - key.time) / (nkey.time - key.time));
- }
- }
- else {
- sum += key.value * (time - key.time);
- }
- }
- return sum;
- }
- public function sample(numPts: Int) {
- var vals = [];
- var duration = this.duration;
- for(i in 0...numPts) {
- var v = getVal(duration * i/(numPts-1));
- vals.push(v);
- }
- return vals;
- }
- #if editor
- override function edit( ctx : EditContext ) {
- super.edit(ctx);
- ctx.properties.add(new hide.Element('
- <div class="group" name="Parameters">
- <dl>
- <dt>Loop curve</dt><dd><input type="checkbox" field="loop"/></dd>
- </dl>
- </div>'), this, function(pname) {
- ctx.onChange(this, pname);
- });
- var ce = new hide.comp.CurveEditor(ctx.properties.undo, ctx.properties.element);
- ce.curve = this;
- }
- override function getHideProps() : HideProps {
- return { icon : "paint-brush", name : "Curve" };
- }
- #end
- public static function getCurve(parent : Prefab, name: String, onlyEnabled=true) {
- for(c in parent.children) {
- if(onlyEnabled && !c.enabled) continue;
- if(c.name != name) continue;
- var curve = c.to(Curve);
- if(curve == null) continue;
- return curve;
- }
- return null;
- }
- public static function getCurves(parent: Prefab, prefix: String) {
- var ret = null;
- for(c in parent.children) {
- if(!c.enabled) continue;
- var idx = c.name.indexOf(".");
- var curvePrefix = (idx >= 0) ? c.name.substr(0, idx) : c.name;
- if(curvePrefix != prefix)
- continue;
- var curve = c.to(Curve);
- if(curve == null) continue;
- if (ret == null) ret = [];
- ret.push(curve);
- }
- return ret;
- }
- public static function getGroups(curves: Array<Curve>) {
- var groups : Array<{name: String, items: Array<Curve>}> = [];
- for(c in curves) {
- var prefix = c.name.split(".")[0];
- var g = groups.find(g -> g.name == prefix);
- if(g == null) {
- groups.push({
- name: prefix,
- items: [c]
- });
- }
- else {
- g.items.push(c);
- }
- }
- return groups;
- }
- static inline function findCurve(curves: Array<Curve>, suffix: String) {
- return curves.find(c -> StringTools.endsWith(c.name, suffix));
- }
- public static function getVectorValue(curves: Array<Curve>, defVal: Float=0.0, scale: Float=1.0) : hrt.prefab.fx.Value {
- inline function find(s) {
- return findCurve(curves, s);
- }
- var x = find(".x");
- var y = find(".y");
- var z = find(".z");
- var w = find(".w");
- inline function curveOrVal(c: Curve, defVal: Float) : hrt.prefab.fx.Value {
- return c != null ? (scale != 1.0 ? VCurveScale(c, scale) : VCurve(c)) : VConst(defVal);
- }
- return VVector(
- curveOrVal(x, defVal),
- curveOrVal(y, defVal),
- curveOrVal(z, defVal),
- curveOrVal(w, 1.0));
- }
- public static function getColorValue(curves: Array<Curve>) : hrt.prefab.fx.Value {
- inline function find(s) {
- return findCurve(curves, s);
- }
- var r = find(".r");
- var g = find(".g");
- var b = find(".b");
- var a = find(".a");
- var h = find(".h");
- var s = find(".s");
- var l = find(".l");
- if(h != null || s != null || l != null) {
- return VHsl(
- h != null ? VCurve(h) : VConst(0.0),
- s != null ? VCurve(s) : VConst(1.0),
- l != null ? VCurve(l) : VConst(1.0),
- a != null ? VCurve(a) : VConst(1.0));
- }
- if(a != null && r == null && g == null && b == null)
- return VCurve(a);
- if(a == null && r == null && g == null && b == null)
- return VOne; // White by default
- return VVector(
- r != null ? VCurve(r) : VConst(1.0),
- g != null ? VCurve(g) : VConst(1.0),
- b != null ? VCurve(b) : VConst(1.0),
- a != null ? VCurve(a) : VConst(1.0));
- }
- static var _ = Library.register("curve", Curve);
- }
|