Ver Fonte

wip structure level properties

David Sehnal há 6 anos atrás
pai
commit
a85a4b09d5

+ 2 - 2
src/mol-model-formats/structure/mmcif/bonds/comp.ts

@@ -7,7 +7,7 @@
 
 import { Model } from 'mol-model/structure/model/model'
 import { LinkType } from 'mol-model/structure/model/types'
-import { ModelPropertyDescriptor } from 'mol-model/structure/model/properties/custom';
+import { CustomPropertyDescriptor } from 'mol-model/structure';
 import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif';
 import { Structure, Unit, StructureProperties, StructureElement } from 'mol-model/structure';
 import { Segmentation } from 'mol-data/int';
@@ -18,7 +18,7 @@ export interface ComponentBond {
 }
 
 export namespace ComponentBond {
-    export const Descriptor: ModelPropertyDescriptor = {
+    export const Descriptor: CustomPropertyDescriptor = {
         isStatic: true,
         name: 'chem_comp_bond',
         cifExport: {

+ 2 - 2
src/mol-model-formats/structure/mmcif/bonds/struct_conn.ts

@@ -10,7 +10,7 @@ import { Structure } from 'mol-model/structure'
 import { LinkType } from 'mol-model/structure/model/types'
 import { findEntityIdByAsymId, findAtomIndexByLabelName } from '../util'
 import { Column } from 'mol-data/db'
-import { ModelPropertyDescriptor } from 'mol-model/structure/model/properties/custom';
+import { CustomPropertyDescriptor } from 'mol-model/structure';
 import { mmCIF_Database, mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif';
 import { SortedArray } from 'mol-data/int';
 import { CifWriter } from 'mol-io/writer/cif'
@@ -23,7 +23,7 @@ export interface StructConn {
 }
 
 export namespace StructConn {
-    export const Descriptor: ModelPropertyDescriptor = {
+    export const Descriptor: CustomPropertyDescriptor = {
         isStatic: true,
         name: 'struct_conn',
         cifExport: {

+ 1 - 1
src/mol-model-formats/structure/mmcif/parser.ts

@@ -13,7 +13,7 @@ import { RuntimeContext } from 'mol-task';
 import UUID from 'mol-util/uuid';
 import { Model } from 'mol-model/structure/model/model';
 import { Entities } from 'mol-model/structure/model/properties/common';
-import { CustomProperties } from 'mol-model/structure/model/properties/custom';
+import { CustomProperties } from 'mol-model/structure';
 import { ModelSymmetry } from 'mol-model/structure/model/properties/symmetry';
 import { createAssemblies } from './assembly';
 import { getAtomicHierarchyAndConformation } from './atomic';

+ 2 - 2
src/mol-model-props/common/custom-element-property.ts

@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { ElementIndex, Model, ModelPropertyDescriptor } from 'mol-model/structure';
+import { ElementIndex, Model, CustomPropertyDescriptor } from 'mol-model/structure';
 import { StructureElement } from 'mol-model/structure/structure';
 import { Location } from 'mol-model/location';
 import { CustomPropertyRegistry } from './custom-property-registry';
@@ -36,7 +36,7 @@ namespace CustomElementProperty {
     export function create<T>(params: CreateParams<T>) {
         const name = params.name;
 
-        const Descriptor = ModelPropertyDescriptor({
+        const Descriptor = CustomPropertyDescriptor({
             isStatic: params.isStatic,
             name: params.name,
         });

+ 2 - 2
src/mol-model-props/common/custom-property-registry.ts

@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { ModelPropertyDescriptor, Model } from 'mol-model/structure';
+import { CustomPropertyDescriptor, Model } from 'mol-model/structure';
 import { OrderedMap } from 'immutable';
 import { ParamDefinition } from 'mol-util/param-definition';
 import { Task } from 'mol-task';
@@ -58,7 +58,7 @@ namespace CustomPropertyRegistry {
     export interface Provider {
         option: [string, string],
         defaultSelected: boolean,
-        descriptor: ModelPropertyDescriptor<any, any>,
+        descriptor: CustomPropertyDescriptor<any, any>,
         attachableTo: (model: Model) => boolean,
         attach: (model: Model) => Task<boolean>
     }

+ 2 - 2
src/mol-model-props/pdbe/preferred-assembly.ts

@@ -7,7 +7,7 @@
 import { Column, Table } from 'mol-data/db';
 import { toTable } from 'mol-io/reader/cif/schema';
 import { CifWriter } from 'mol-io/writer/cif';
-import { Model, ModelPropertyDescriptor } from 'mol-model/structure';
+import { Model, CustomPropertyDescriptor } from 'mol-model/structure';
 
 export namespace PDBePreferredAssembly {
     export type Property = string
@@ -31,7 +31,7 @@ export namespace PDBePreferredAssembly {
     };
     export type Schema = typeof Schema
 
-    export const Descriptor = ModelPropertyDescriptor({
+    export const Descriptor = CustomPropertyDescriptor({
         isStatic: true,
         name: 'pdbe_preferred_assembly',
         cifExport: {

+ 2 - 2
src/mol-model-props/pdbe/struct-ref-domain.ts

@@ -7,7 +7,7 @@
 import { Column, Table } from 'mol-data/db';
 import { toTable } from 'mol-io/reader/cif/schema';
 import { CifWriter } from 'mol-io/writer/cif';
-import { Model, ModelPropertyDescriptor } from 'mol-model/structure';
+import { Model, CustomPropertyDescriptor } from 'mol-model/structure';
 import { PropertyWrapper } from '../common/wrapper';
 
 export namespace PDBeStructRefDomain {
@@ -39,7 +39,7 @@ export namespace PDBeStructRefDomain {
     };
     export type Schema = typeof Schema
 
-    export const Descriptor = ModelPropertyDescriptor({
+    export const Descriptor = CustomPropertyDescriptor({
         isStatic: true,
         name: 'pdbe_struct_ref_domain',
         cifExport: {

+ 2 - 2
src/mol-model-props/pdbe/structure-quality-report.ts

@@ -8,7 +8,7 @@ import { Column, Table } from 'mol-data/db';
 import { toTable } from 'mol-io/reader/cif/schema';
 import { mmCIF_residueId_schema } from 'mol-io/reader/cif/schema/mmcif-extras';
 import { CifWriter } from 'mol-io/writer/cif';
-import { Model, ModelPropertyDescriptor, ResidueIndex, Unit, IndexedCustomProperty } from 'mol-model/structure';
+import { Model, CustomPropertyDescriptor, ResidueIndex, Unit, IndexedCustomProperty } from 'mol-model/structure';
 import { residueIdFields } from 'mol-model/structure/export/categories/atom_site';
 import { StructureElement, CifExportContext } from 'mol-model/structure/structure';
 import { CustomPropSymbol } from 'mol-script/language/symbol';
@@ -43,7 +43,7 @@ export namespace StructureQualityReport {
     };
     export type Schema = typeof Schema
 
-    export const Descriptor = ModelPropertyDescriptor({
+    export const Descriptor = CustomPropertyDescriptor({
         isStatic: false,
         name: 'pdbe_structure_quality_report',
         cifExport: {

+ 2 - 2
src/mol-model-props/rcsb/assembly-symmetry.ts

@@ -7,7 +7,7 @@
 import { AssemblySymmetry as AssemblySymmetryGraphQL } from './graphql/types';
 import query from './graphql/symmetry.gql';
 
-import { Model, ModelPropertyDescriptor } from 'mol-model/structure';
+import { Model, CustomPropertyDescriptor } from 'mol-model/structure';
 import { CifWriter } from 'mol-io/writer/cif';
 import { Database as _Database, Column, Table } from 'mol-data/db'
 import { Category } from 'mol-io/writer/cif/encoder';
@@ -140,7 +140,7 @@ function createDatabaseFromCif(model: Model): AssemblySymmetry.Database {
     })
 }
 
-const _Descriptor: ModelPropertyDescriptor = {
+const _Descriptor: CustomPropertyDescriptor = {
     isStatic: true,
     name: 'rcsb_assembly_symmetry',
     cifExport: {

+ 2 - 1
src/mol-model/structure.ts

@@ -6,4 +6,5 @@
 
 export * from './structure/model'
 export * from './structure/structure'
-export * from './structure/query'
+export * from './structure/query'
+export * from './structure/common/custom-property'

+ 60 - 0
src/mol-model/structure/common/custom-property.ts

@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { CifWriter } from 'mol-io/writer/cif'
+import { CifExportContext } from '../export/mmcif';
+import { QuerySymbolRuntime } from 'mol-script/runtime/query/compiler';
+import { UUID } from 'mol-util';
+
+export { CustomPropertyDescriptor, CustomProperties }
+
+interface CustomPropertyDescriptor<ExportCtx = CifExportContext, Symbols extends { [name: string]: QuerySymbolRuntime } = { }> {
+    readonly isStatic: boolean,
+    readonly name: string,
+
+    cifExport?: {
+        // Prefix enforced during export.
+        prefix: string,
+        context?: (ctx: CifExportContext) => ExportCtx | undefined,
+        categories: CifWriter.Category<ExportCtx>[]
+    },
+
+    // TODO: add aliases when lisp-like mol-script is done
+    symbols?: Symbols
+}
+
+function CustomPropertyDescriptor<Ctx, Desc extends CustomPropertyDescriptor<Ctx>>(desc: Desc) {
+    return desc;
+}
+
+namespace CustomPropertyDescriptor {
+    export function getUUID(prop: CustomPropertyDescriptor): UUID {
+        if (!(prop as any).__key) {
+            (prop as any).__key = UUID.create22();
+        }
+        return (prop as any).__key;
+    }
+}
+
+class CustomProperties {
+    private _list: CustomPropertyDescriptor[] = [];
+    private _set = new Set<CustomPropertyDescriptor>();
+
+    get all(): ReadonlyArray<CustomPropertyDescriptor> {
+        return this._list;
+    }
+
+    add(desc: CustomPropertyDescriptor<any>) {
+        if (this._set.has(desc)) return;
+
+        this._list.push(desc);
+        this._set.add(desc);
+    }
+
+    has(desc: CustomPropertyDescriptor<any>): boolean {
+        return this._set.has(desc);
+    }
+}

+ 2 - 2
src/mol-model/structure/export/mmcif.ts

@@ -16,7 +16,7 @@ import { _chem_comp, _pdbx_chem_comp_identifier, _pdbx_nonpoly_scheme } from './
 import { Model } from '../model';
 import { getUniqueEntityIndicesFromStructures, copy_mmCif_category } from './categories/utils';
 import { _struct_asym, _entity_poly, _entity_poly_seq } from './categories/sequence';
-import { ModelPropertyDescriptor } from '../model/properties/custom';
+import { CustomPropertyDescriptor } from '../common/custom-property';
 
 export interface CifExportContext {
     structures: Structure[],
@@ -122,7 +122,7 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structures:
 
         let propCtx = ctx;
         if (customProp.cifExport.context) {
-            const propId = ModelPropertyDescriptor.getUUID(customProp);
+            const propId = CustomPropertyDescriptor.getUUID(customProp);
             if (ctx.cache[propId + '__ctx']) propCtx = ctx.cache[propId + '__ctx'];
             else {
                 propCtx = customProp.cifExport.context(ctx) || ctx;

+ 1 - 1
src/mol-model/structure/model.ts

@@ -9,6 +9,6 @@ import * as Types from './model/types'
 import { ModelSymmetry } from './model/properties/symmetry'
 import StructureSequence from './model/properties/sequence'
 
-export * from './model/properties/custom'
+export * from './model/properties/custom/indexed'
 export * from './model/indexing'
 export { Model, Types, ModelSymmetry, StructureSequence }

+ 1 - 1
src/mol-model/structure/model/model.ts

@@ -10,7 +10,7 @@ import { AtomicHierarchy, AtomicConformation } from './properties/atomic';
 import { ModelSymmetry } from './properties/symmetry';
 import { CoarseHierarchy, CoarseConformation } from './properties/coarse';
 import { Entities } from './properties/common';
-import { CustomProperties } from './properties/custom';
+import { CustomProperties } from '../common/custom-property';
 import { SecondaryStructure } from './properties/seconday-structure';
 import { SaccharideComponentMap } from '../structure/carbohydrates/constants';
 import { ModelFormat } from 'mol-model-formats/structure/format';

+ 0 - 9
src/mol-model/structure/model/properties/custom.ts

@@ -1,9 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-export * from './custom/descriptor'
-export * from './custom/collection'
-export * from './custom/indexed'

+ 0 - 91
src/mol-model/structure/model/properties/custom/chain.ts

@@ -1,91 +0,0 @@
-// /**
-//  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
-//  *
-//  * @author David Sehnal <david.sehnal@gmail.com>
-//  */
-
-// import { ChainIndex } from '../../indexing';
-// import { Unit, Structure, StructureElement } from '../../../structure';
-// import { Segmentation } from 'mol-data/int';
-// import { UUID } from 'mol-util';
-// import { CifWriter } from 'mol-io/writer/cif';
-
-// export interface ChainCustomProperty<T = any> {
-//     readonly id: UUID,
-//     readonly kind: Unit.Kind,
-//     has(idx: ChainIndex): boolean
-//     get(idx: ChainIndex): T | undefined
-// }
-
-// export namespace ChainCustomProperty {
-//     export interface ExportCtx<T> {
-//         elements: StructureElement[],
-//         property(index: number): T
-//     };
-
-//     function getExportCtx<T>(prop: ChainCustomProperty<T>, structure: Structure): ExportCtx<T> {
-//         const chainIndex = structure.model.atomicHierarchy.chainAtomSegments.index;
-//         const elements = getStructureElements(structure, prop);
-//         return { elements, property: i => prop.get(chainIndex[elements[i].element])! };
-//     }
-
-//     export function getCifDataSource<T>(structure: Structure, prop: ChainCustomProperty<T> | undefined, cache: any): CifWriter.Category.Instance['source'][0] {
-//         if (!prop) return { rowCount: 0 };
-//         if (cache && cache[prop.id]) return cache[prop.id];
-//         const data = getExportCtx(prop, structure);
-//         const ret = { data, rowCount: data.elements.length };
-//         if (cache) cache[prop.id] = ret;
-//         return ret;
-//     }
-
-//     class FromMap<T> implements ChainCustomProperty<T> {
-//         readonly id = UUID.create();
-
-//         has(idx: ChainIndex): boolean {
-//             return this.map.has(idx);
-//         }
-
-//         get(idx: ChainIndex) {
-//             return this.map.get(idx);
-//         }
-
-//         constructor(private map: Map<ChainIndex, T>, public kind: Unit.Kind) {
-//         }
-//     }
-
-//     export function fromMap<T>(map: Map<ChainIndex, T>, kind: Unit.Kind) {
-//         return new FromMap(map, kind);
-//     }
-
-//     /**
-//      * Gets all StructureElements that correspond to 1st atoms of residues that have an property assigned.
-//      * Only works correctly for structures with a single model.
-//      */
-//     export function getStructureElements(structure: Structure, property: ChainCustomProperty) {
-//         const models = structure.models;
-//         if (models.length !== 1) throw new Error(`Only works on structures with a single model.`);
-
-//         const seenChains = new Set<ChainIndex>();
-//         const unitGroups = structure.unitSymmetryGroups;
-//         const loci: StructureElement[] = [];
-
-//         for (const unitGroup of unitGroups) {
-//             const unit = unitGroup.units[0];
-//             if (unit.kind !== property.kind) {
-//                 continue;
-//             }
-
-//             const chains = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, unit.elements);
-//             while (chains.hasNext) {
-//                 const seg = chains.move();
-//                 if (!property.has(seg.index) || seenChains.has(seg.index)) continue;
-
-//                 seenChains.add(seg.index);
-//                 loci[loci.length] = StructureElement.create(unit, unit.elements[seg.start]);
-//             }
-//         }
-
-//         loci.sort((x, y) => x.element - y.element);
-//         return loci;
-//     }
-// }

+ 0 - 27
src/mol-model/structure/model/properties/custom/collection.ts

@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { ModelPropertyDescriptor } from './descriptor'
-
-export class CustomProperties {
-    private _list: ModelPropertyDescriptor[] = [];
-    private _set = new Set<ModelPropertyDescriptor>();
-
-    get all(): ReadonlyArray<ModelPropertyDescriptor> {
-        return this._list;
-    }
-
-    add(desc: ModelPropertyDescriptor<any>) {
-        if (this._set.has(desc)) return;
-
-        this._list.push(desc);
-        this._set.add(desc);
-    }
-
-    has(desc: ModelPropertyDescriptor<any>): boolean {
-        return this._set.has(desc);
-    }
-}

+ 0 - 40
src/mol-model/structure/model/properties/custom/descriptor.ts

@@ -1,40 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { CifWriter } from 'mol-io/writer/cif'
-import { CifExportContext } from '../../../export/mmcif';
-import { QuerySymbolRuntime } from 'mol-script/runtime/query/compiler';
-import { UUID } from 'mol-util';
-
-interface ModelPropertyDescriptor<ExportCtx = CifExportContext, Symbols extends { [name: string]: QuerySymbolRuntime } = { }> {
-    readonly isStatic: boolean,
-    readonly name: string,
-
-    cifExport?: {
-        // Prefix enforced during export.
-        prefix: string,
-        context?: (ctx: CifExportContext) => ExportCtx | undefined,
-        categories: CifWriter.Category<ExportCtx>[]
-    },
-
-    // TODO: add aliases when lisp-like mol-script is done
-    symbols?: Symbols
-}
-
-function ModelPropertyDescriptor<Ctx, Desc extends ModelPropertyDescriptor<Ctx>>(desc: Desc) {
-    return desc;
-}
-
-namespace ModelPropertyDescriptor {
-    export function getUUID(prop: ModelPropertyDescriptor): UUID {
-        if (!(prop as any).__key) {
-            (prop as any).__key = UUID.create22();
-        }
-        return (prop as any).__key;
-    }
-}
-
-export { ModelPropertyDescriptor }

+ 0 - 91
src/mol-model/structure/model/properties/custom/residue.ts

@@ -1,91 +0,0 @@
-// /**
-//  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
-//  *
-//  * @author David Sehnal <david.sehnal@gmail.com>
-//  */
-
-// import { ResidueIndex } from '../../indexing';
-// import { Unit, Structure, StructureElement } from '../../../structure';
-// import { Segmentation } from 'mol-data/int';
-// import { UUID } from 'mol-util';
-// import { CifWriter } from 'mol-io/writer/cif';
-
-// export interface ResidueCustomProperty<T = any> {
-//     readonly id: UUID,
-//     readonly kind: Unit.Kind,
-//     has(idx: ResidueIndex): boolean
-//     get(idx: ResidueIndex): T | undefined
-// }
-
-// export namespace ResidueCustomProperty {
-//     export interface ExportCtx<T> {
-//         elements: StructureElement[],
-//         property(index: number): T
-//     };
-
-//     function getExportCtx<T>(prop: ResidueCustomProperty<T>, structure: Structure): ExportCtx<T> {
-//         const residueIndex = structure.model.atomicHierarchy.residueAtomSegments.index;
-//         const elements = getStructureElements(structure, prop);
-//         return { elements, property: i => prop.get(residueIndex[elements[i].element])! };
-//     }
-
-//     export function getCifDataSource<T>(structure: Structure, prop: ResidueCustomProperty<T> | undefined, cache: any): CifWriter.Category.Instance['source'][0] {
-//         if (!prop) return { rowCount: 0 };
-//         if (cache && cache[prop.id]) return cache[prop.id];
-//         const data = getExportCtx(prop, structure);
-//         const ret = { data, rowCount: data.elements.length };
-//         if (cache) cache[prop.id] = ret;
-//         return ret;
-//     }
-
-//     class FromMap<T> implements ResidueCustomProperty<T> {
-//         readonly id = UUID.create();
-
-//         has(idx: ResidueIndex): boolean {
-//             return this.map.has(idx);
-//         }
-
-//         get(idx: ResidueIndex) {
-//             return this.map.get(idx);
-//         }
-
-//         constructor(private map: Map<ResidueIndex, T>, public kind: Unit.Kind) {
-//         }
-//     }
-
-//     export function fromMap<T>(map: Map<ResidueIndex, T>, kind: Unit.Kind) {
-//         return new FromMap(map, kind);
-//     }
-
-//     /**
-//      * Gets all StructureElements that correspond to 1st atoms of residues that have an property assigned.
-//      * Only works correctly for structures with a single model.
-//      */
-//     export function getStructureElements(structure: Structure, property: ResidueCustomProperty) {
-//         const models = structure.models;
-//         if (models.length !== 1) throw new Error(`Only works on structures with a single model.`);
-
-//         const seenResidues = new Set<ResidueIndex>();
-//         const unitGroups = structure.unitSymmetryGroups;
-//         const loci: StructureElement[] = [];
-
-//         for (const unitGroup of unitGroups) {
-//             const unit = unitGroup.units[0];
-//             if (unit.kind !== property.kind) {
-//                 continue;
-//             }
-
-//             const residues = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements);
-//             while (residues.hasNext) {
-//                 const seg = residues.move();
-//                 if (!property.has(seg.index) || seenResidues.has(seg.index)) continue;
-
-//                 seenResidues.add(seg.index);
-//                 loci[loci.length] = StructureElement.create(unit, unit.elements[seg.start]);
-//             }
-//         }
-
-//         loci.sort((x, y) => x.element - y.element);
-//         return loci;
-//     }
-// }

+ 2 - 2
src/mol-script/runtime/query/compiler.ts

@@ -5,7 +5,7 @@
  */
 
 import Expression from '../../language/expression';
-import { QueryContext, QueryFn, Structure, ModelPropertyDescriptor } from 'mol-model/structure';
+import { QueryContext, QueryFn, Structure, CustomPropertyDescriptor } from 'mol-model/structure';
 import { MSymbol } from '../../language/symbol';
 
 export class QueryRuntimeTable {
@@ -18,7 +18,7 @@ export class QueryRuntimeTable {
         this.map.set(runtime.symbol.id, runtime);
     }
 
-    addCustomProp(desc: ModelPropertyDescriptor<any>) {
+    addCustomProp(desc: CustomPropertyDescriptor<any>) {
         if (!desc.symbols) return;
 
         for (const k of Object.keys(desc.symbols)) {

+ 2 - 2
src/perf-tests/mol-script.ts

@@ -1,6 +1,6 @@
 import { MolScriptBuilder } from 'mol-script/language/builder';
 import { compile, QuerySymbolRuntime, DefaultQueryRuntimeTable } from 'mol-script/runtime/query/compiler';
-import { QueryContext, Structure, StructureQuery, ModelPropertyDescriptor } from 'mol-model/structure';
+import { QueryContext, Structure, StructureQuery, CustomPropertyDescriptor } from 'mol-model/structure';
 import { readCifFile, getModelsAndStructure } from '../apps/structure-info/model';
 import { CustomPropSymbol } from 'mol-script/language/symbol';
 import Type from 'mol-script/language/type';
@@ -46,7 +46,7 @@ const compiled = compile<number>(expr);
 const result = compiled(new QueryContext(Structure.Empty));
 console.log(result);
 
-const CustomProp = ModelPropertyDescriptor({
+const CustomProp = CustomPropertyDescriptor({
     name: 'test_prop',
     isStatic: true,
     cifExport: { prefix: '', categories: [ ]},