scripts/path-controller/controller/controller_ops.js
import {ToolOp, ToolFlags} from "../toolsys/toolsys.js";
import {
PropTypes, PropFlags, BoolProperty, IntProperty, FloatProperty, FlagProperty,
EnumProperty, StringProperty, Vec3Property, Vec2Property, Vec4Property,
QuatProperty, Mat4Property
} from "../toolsys/toolprop.js";
import * as util from '../util/util.js';
import {isVecProperty, getVecClass} from "./controller_base.js";
export class DataPathSetOp extends ToolOp {
constructor() {
super();
this.propType = -1;
this._undo = undefined;
}
setValue(ctx, val, object) {
let prop = this.inputs.prop;
let path = this.inputs.dataPath.getValue();
if (prop.type & (PropTypes.ENUM | PropTypes.FLAG)) {
let rdef = ctx.api.resolvePath(ctx, path);
if (rdef.subkey !== undefined) {
let subkey = rdef.subkey;
if (typeof subkey === "string") {
subkey = rdef.prop.values[subkey];
}
this.inputs.flagBit.setValue(subkey);
this.inputs.useFlagBit.setValue(true);
}
//if (rdef.subkey !== undefined) {
// val = rdef.value;
//val = !!val;
//}
}
prop.dataref = object;
prop.ctx = ctx;
prop.datapath = path;
try {
prop.setValue(val);
this.hadError = false;
} catch (error) {
console.error("Error setting datapath", path);
this.hadError = true;
}
}
static create(ctx, datapath, value, id, massSetPath) {
let rdef = ctx.api.resolvePath(ctx, datapath);
if (rdef === undefined || rdef.prop === undefined) {
console.warn("DataPathSetOp failed", rdef, rdef.prop);
return;
}
let prop = rdef.prop;
let tool = new DataPathSetOp();
tool.propType = prop.type;
tool.inputs.destType.setValue(prop.type);
if (prop && (prop.flag & PropFlags.USE_BASE_UNDO)) {
tool.inputs.fullSaveUndo.setValue(true);
}
let mask = PropTypes.FLAG | PropTypes.ENUM;
mask |= PropTypes.VEC2 | PropTypes.VEC3 | PropTypes.VEC4 | PropTypes.QUAT;
if (rdef.subkey !== undefined && (prop.type & mask)) {
if (prop.type & (PropTypes.ENUM | PropTypes.FLAG)) {
let i = datapath.length - 1;
//chope off enum selector
while (i >= 0 && datapath[i] !== '[') {
i--;
}
if (i >= 0) {
datapath = datapath.slice(0, i);
}
tool.inputs.prop = new IntProperty();
} else {
tool.inputs.prop = new FloatProperty();
}
let subkey = rdef.subkey;
if (typeof subkey !== "number") {
subkey = rdef.prop.values[subkey];
}
if (prop.type === PropTypes.FLAG) {
tool.inputs.flagBit.setValue(subkey);
tool.inputs.useFlagBit.setValue(true);
}
if (prop.type === PropTypes.ENUM) {
value = subkey;
} else if (prop.type === PropTypes.FLAG) {
let value2 = ctx.api.getValue(ctx, datapath);
if (typeof value2 !== "number") {
value2 = typeof value2 === "boolean" ? (value & 1) : 0;
}
if (value) {
value2 |= subkey;
} else {
value2 &= ~subkey;
}
value = value2;
}
//value = rdef.obj[rdef.key];
//console.log("rdef.value", value);
} else {
tool.inputs.prop = prop.copy();
}
tool.inputs.dataPath.setValue(datapath);
if (massSetPath) {
tool.inputs.massSetPath.setValue(massSetPath);
} else {
tool.inputs.massSetPath.setValue("");
}
tool.id = id;
tool.setValue(ctx, value, rdef.obj);
return tool;
}
hash(massSetPath, dataPath, prop, id) {
massSetPath = massSetPath === undefined ? "" : massSetPath;
massSetPath = massSetPath === null ? "" : massSetPath;
let ret = "" + massSetPath + ":" + dataPath + ":" + prop + ":" + id;
return ret;
}
hashThis() {
return this.hash(this.inputs.massSetPath.getValue(),
this.inputs.dataPath.getValue(),
this.propType,
this.id);
}
undoPre(ctx) {
if (this.inputs.fullSaveUndo.getValue()) {
return super.undoPre(ctx);
}
if (this.__ctx)
ctx = this.__ctx;
this._undo = {};
let paths = new Set();
if (this.inputs.massSetPath.getValue().trim()) {
let massSetPath = this.inputs.massSetPath.getValue().trim();
paths = new Set(ctx.api.resolveMassSetPaths(ctx, massSetPath));
}
paths.add(this.inputs.dataPath.getValue());
for (let path of paths) {
let val = ctx.api.getValue(ctx, path);
if (typeof val === "object") {
val = val.copy();
}
this._undo[path] = val;
}
/*
for (let path of paths) {
let rdef = ctx.api.resolvePath(ctx, path);
if (rdef === undefined) {
console.warn("Failed to lookup path in DataPathSetOp.undoPre", path);
continue;
}
let prop = rdef.prop;
let value = rdef.value;
if (prop.type & (PropTypes.ENUM|PropTypes.FLAG)) {
this._undo[path] = rdef.obj[rdef.key];
} else if (isVecProperty(prop)) {
if (rdef.subkey) {
this._undo[path] = rdef.value;
} else {
let cls = getVecClass(prop.type);
this._undo[path] = new cls(rdef.value);
}
} else {
let prop2 = prop.copy();
prop2.dataref = rdef.obj;
prop2.datapath = path;
prop2.ctx = ctx;
prop2.setValue(value);
this._undo[path] = prop2.getValue();
}
}*/
}
undo(ctx) {
if (this.__ctx)
ctx = this.__ctx;
if (this.inputs.fullSaveUndo.getValue()) {
return super.undo(ctx);
}
for (let path in this._undo) {
let rdef = ctx.api.resolvePath(ctx, path);
if (rdef.prop !== undefined && (rdef.prop.type & (PropTypes.ENUM | PropTypes.FLAG))) {
let old = rdef.obj[rdef.key];
if (rdef.subkey) {
let key = rdef.subkey;
if (typeof key !== "number") {
key = rdef.prop.values[key];
}
if (rdef.prop.type === PropTypes.FLAG) {
if (this._undo[path]) {
rdef.obj[rdef.key] |= key;
} else {
rdef.obj[rdef.key] &= ~key;
}
} else {
rdef.obj[rdef.key] = key;
}
} else {
rdef.obj[rdef.key] = this._undo[path];
}
rdef.prop.dataref = rdef.obj;
rdef.prop.datapath = path;
rdef.prop.ctx = ctx;
rdef.prop._fire("change", rdef.obj[rdef.key], old);
} else {
try {
ctx.api.setValue(ctx, path, this._undo[path]);
} catch (error) {
util.print_stack(error);
console.warn("Failed to set property in undo for DataPathSetOp");
}
}
}
}
exec(ctx) {
//use saved ctx we got from modal start
if (this.__ctx) {
ctx = this.__ctx;
}
let path = this.inputs.dataPath.getValue();
let massSetPath = this.inputs.massSetPath.getValue().trim();
try {
ctx.api.setValue(ctx, path, this.inputs.prop.getValue());
this.hadError = false;
} catch (error) {
console.log(error.stack);
console.log(error.message);
console.log("error setting " + path);
this.hadError = true;
}
if (massSetPath) {
let value = this.inputs.prop.getValue();
let useFlagBit = this.inputs.useFlagBit.getValue();
if (useFlagBit && this.inputs.destType.getValue() === PropTypes.FLAG) {
let bit = this.inputs.flagBit.getValue();
value = !!(value & bit);
}
try {
ctx.api.massSetProp(ctx, massSetPath, value);
} catch (error) {
console.log(error.stack);
console.log(error.message);
console.log("error setting " + path);
this.hadError = true;
}
}
}
modalStart(ctx) {
this.__ctx = ctx.toLocked();
//save full, modal ctx
super.modalStart(this.__ctx);
this.exec(this.__ctx);
this.modalEnd(false);
}
static tooldef() {
return {
uiname : "Property Set",
toolpath: "app.prop_set",
icon : -1,
flag : ToolFlags.PRIVATE,
is_modal: true,
inputs : {
dataPath : new StringProperty(),
massSetPath : new StringProperty(),
fullSaveUndo: new BoolProperty(false),
flagBit : new IntProperty(),
useFlagBit : new BoolProperty(),
destType : new EnumProperty(PropTypes.INT, PropTypes),
}
}
}
}
ToolOp.register(DataPathSetOp);