|
@@ -1,25 +1,48 @@
|
|
|
/**
|
|
|
- * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
|
|
+ * Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
|
|
*
|
|
|
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
|
|
*/
|
|
|
|
|
|
+import { Quat, Vec3 } from '../mol-math/linear-algebra';
|
|
|
+import { degToRad } from '../mol-math/misc';
|
|
|
import { Loci } from '../mol-model/loci';
|
|
|
import { StructureElement, Structure } from '../mol-model/structure';
|
|
|
import { Script } from '../mol-script/script';
|
|
|
import { BitFlags } from '../mol-util/bit-flags';
|
|
|
+import { ParamDefinition as PD } from '../mol-util/param-definition';
|
|
|
+import { stringToWords } from '../mol-util/string';
|
|
|
|
|
|
export { Clipping };
|
|
|
|
|
|
-type Clipping = { readonly layers: ReadonlyArray<Clipping.Layer> }
|
|
|
+type Clipping = {
|
|
|
+ readonly layers: ReadonlyArray<Clipping.Layer>
|
|
|
+ readonly variant: Clipping.Variant
|
|
|
+ readonly objects: Clipping.Objects
|
|
|
+}
|
|
|
+
|
|
|
+function Clipping(layers: Clipping['layers'], variant: Clipping['variant'], objects: Clipping['objects']): Clipping {
|
|
|
+ return { layers, variant, objects };
|
|
|
+}
|
|
|
|
|
|
-function Clipping(layers: ReadonlyArray<Clipping.Layer>): Clipping {
|
|
|
- return { layers };
|
|
|
+function createClipObjects() {
|
|
|
+ return {
|
|
|
+ count: 0,
|
|
|
+ type: (new Array(5)).fill(1),
|
|
|
+ invert: (new Array(5)).fill(false),
|
|
|
+ position: (new Array(5 * 3)).fill(0),
|
|
|
+ rotation: (new Array(5 * 4)).fill(0),
|
|
|
+ scale: (new Array(5 * 3)).fill(1),
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
namespace Clipping {
|
|
|
export type Layer = { readonly loci: StructureElement.Loci, readonly groups: Groups }
|
|
|
- export const Empty: Clipping = { layers: [] };
|
|
|
+ export const Empty: Clipping = {
|
|
|
+ layers: [],
|
|
|
+ variant: 'pixel',
|
|
|
+ objects: createClipObjects()
|
|
|
+ };
|
|
|
|
|
|
export type Groups = BitFlags<Groups.Flag>
|
|
|
export namespace Groups {
|
|
@@ -95,21 +118,93 @@ namespace Clipping {
|
|
|
|
|
|
export type Variant = 'instance' | 'pixel'
|
|
|
|
|
|
+ export type Objects = {
|
|
|
+ count: number
|
|
|
+ type: number[]
|
|
|
+ invert: boolean[]
|
|
|
+ position: number[]
|
|
|
+ rotation: number[]
|
|
|
+ scale: number[]
|
|
|
+ }
|
|
|
+
|
|
|
+ export const Params = {
|
|
|
+ variant: PD.Select('instance', PD.arrayToOptions<Variant>(['instance', 'pixel'])),
|
|
|
+ objects: PD.ObjectList({
|
|
|
+ type: PD.Select('plane', PD.objectToOptions(Type, t => stringToWords(t))),
|
|
|
+ invert: PD.Boolean(false),
|
|
|
+ position: PD.Vec3(Vec3()),
|
|
|
+ rotation: PD.Group({
|
|
|
+ axis: PD.Vec3(Vec3.create(1, 0, 0)),
|
|
|
+ angle: PD.Numeric(0, { min: -180, max: 180, step: 1 }, { description: 'Angle in Degrees' }),
|
|
|
+ }, { isExpanded: true }),
|
|
|
+ scale: PD.Vec3(Vec3.create(1, 1, 1)),
|
|
|
+ }, o => stringToWords(o.type))
|
|
|
+ };
|
|
|
+ export type Params = typeof Params
|
|
|
+ export type Props = PD.Values<Params>
|
|
|
+
|
|
|
+ export type Clip = {
|
|
|
+ variant: Clipping['variant'],
|
|
|
+ objects: Clipping['objects']
|
|
|
+ }
|
|
|
+
|
|
|
+ const qA = Quat();
|
|
|
+ const qB = Quat();
|
|
|
+ const vA = Vec3();
|
|
|
+ const vB = Vec3();
|
|
|
+
|
|
|
+ export function getClip(props: Props, clip?: Clip): Clip {
|
|
|
+ const { type, invert, position, rotation, scale } = clip?.objects || createClipObjects();
|
|
|
+ for (let i = 0, il = props.objects.length; i < il; ++i) {
|
|
|
+ const p = props.objects[i];
|
|
|
+ type[i] = Type[p.type];
|
|
|
+ invert[i] = p.invert;
|
|
|
+ Vec3.toArray(p.position, position, i * 3);
|
|
|
+ Quat.toArray(Quat.setAxisAngle(qA, p.rotation.axis, degToRad(p.rotation.angle)), rotation, i * 4);
|
|
|
+ Vec3.toArray(p.scale, scale, i * 3);
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ variant: props.variant,
|
|
|
+ objects: { count: props.objects.length, type, invert, position, rotation, scale }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
export function areEqual(cA: Clipping, cB: Clipping) {
|
|
|
- if (cA.layers.length === 0 && cB.layers.length === 0) return true;
|
|
|
if (cA.layers.length !== cB.layers.length) return false;
|
|
|
for (let i = 0, il = cA.layers.length; i < il; ++i) {
|
|
|
if (cA.layers[i].groups !== cB.layers[i].groups) return false;
|
|
|
if (!Loci.areEqual(cA.layers[i].loci, cB.layers[i].loci)) return false;
|
|
|
}
|
|
|
+ if (cA.variant !== cB.variant) return false;
|
|
|
+ if (cA.objects.count !== cB.objects.count) return false;
|
|
|
+
|
|
|
+ const oA = cA.objects, oB = cB.objects;
|
|
|
+ for (let i = 0, il = oA.count; i < il; ++i) {
|
|
|
+ if (oA.invert[i] !== oB.invert[i]) return false;
|
|
|
+ if (oA.type[i] !== oB.type[i]) return false;
|
|
|
+
|
|
|
+ Vec3.fromArray(vA, oA.position, i * 3);
|
|
|
+ Vec3.fromArray(vB, oB.position, i * 3);
|
|
|
+ if (!Vec3.equals(vA, vB)) return false;
|
|
|
+
|
|
|
+ Vec3.fromArray(vA, oA.scale, i * 3);
|
|
|
+ Vec3.fromArray(vB, oB.scale, i * 3);
|
|
|
+ if (!Vec3.equals(vA, vB)) return false;
|
|
|
+
|
|
|
+ Quat.fromArray(qA, oA.rotation, i * 4);
|
|
|
+ Quat.fromArray(qB, oB.rotation, i * 4);
|
|
|
+ if (!Quat.equals(qA, qB)) return false;
|
|
|
+ }
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+ /** Check if layers empty */
|
|
|
export function isEmpty(clipping: Clipping) {
|
|
|
return clipping.layers.length === 0;
|
|
|
}
|
|
|
|
|
|
- export function remap(clipping: Clipping, structure: Structure) {
|
|
|
+ /** Remap layers */
|
|
|
+ export function remap(clipping: Clipping, structure: Structure): Clipping {
|
|
|
const layers: Clipping.Layer[] = [];
|
|
|
for (const layer of clipping.layers) {
|
|
|
let { loci, groups } = layer;
|
|
@@ -118,9 +213,10 @@ namespace Clipping {
|
|
|
layers.push({ loci, groups });
|
|
|
}
|
|
|
}
|
|
|
- return { layers };
|
|
|
+ return { layers, variant: clipping.variant, objects: clipping.objects };
|
|
|
}
|
|
|
|
|
|
+ /** Merge layers */
|
|
|
export function merge(clipping: Clipping): Clipping {
|
|
|
if (isEmpty(clipping)) return clipping;
|
|
|
const { structure } = clipping.layers[0].loci;
|
|
@@ -141,9 +237,10 @@ namespace Clipping {
|
|
|
map.forEach((loci, groups) => {
|
|
|
layers.push({ loci, groups });
|
|
|
});
|
|
|
- return { layers };
|
|
|
+ return { layers, variant: clipping.variant, objects: clipping.objects };
|
|
|
}
|
|
|
|
|
|
+ /** Filter layers */
|
|
|
export function filter(clipping: Clipping, filter: Structure): Clipping {
|
|
|
if (isEmpty(clipping)) return clipping;
|
|
|
const { structure } = clipping.layers[0].loci;
|
|
@@ -158,11 +255,11 @@ namespace Clipping {
|
|
|
layers.push({ loci, groups });
|
|
|
}
|
|
|
}
|
|
|
- return { layers };
|
|
|
+ return { layers, variant: clipping.variant, objects: clipping.objects };
|
|
|
}
|
|
|
|
|
|
export type ScriptLayer = { script: Script, groups: Groups }
|
|
|
- export function ofScript(scriptLayers: ScriptLayer[], structure: Structure): Clipping {
|
|
|
+ export function ofScript(scriptLayers: ScriptLayer[], structure: Structure, clip: Clip): Clipping {
|
|
|
const layers: Clipping.Layer[] = [];
|
|
|
for (let i = 0, il = scriptLayers.length; i < il; ++i) {
|
|
|
const { script, groups } = scriptLayers[i];
|
|
@@ -171,18 +268,18 @@ namespace Clipping {
|
|
|
layers.push({ loci, groups });
|
|
|
}
|
|
|
}
|
|
|
- return { layers };
|
|
|
+ return { layers, variant: clip.variant, objects: clip.objects };
|
|
|
}
|
|
|
|
|
|
export type BundleLayer = { bundle: StructureElement.Bundle, groups: Groups }
|
|
|
- export function ofBundle(bundleLayers: BundleLayer[], structure: Structure): Clipping {
|
|
|
+ export function ofBundle(bundleLayers: BundleLayer[], structure: Structure, clip: Clip): Clipping {
|
|
|
const layers: Clipping.Layer[] = [];
|
|
|
for (let i = 0, il = bundleLayers.length; i < il; ++i) {
|
|
|
const { bundle, groups } = bundleLayers[i];
|
|
|
const loci = StructureElement.Bundle.toLoci(bundle, structure.root);
|
|
|
layers.push({ loci, groups });
|
|
|
}
|
|
|
- return { layers };
|
|
|
+ return { layers, variant: clip.variant, objects: clip.objects };
|
|
|
}
|
|
|
|
|
|
export function toBundle(clipping: Clipping) {
|