Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Color the 3D structure by a property #303

Merged
merged 15 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 28 additions & 25 deletions src/structure/grid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import CLOSE_SVG from '../static/close.svg';
import DUPLICATE_SVG from '../static/duplicate.svg';
import PNG_SVG from '../static/download-png.svg';

interface SelectiveEnvironment extends Environment {
Luthaf marked this conversation as resolved.
Show resolved Hide resolved
environment: number;
}

/**
* Create a list of environments grouped together by structure.
*
Expand All @@ -44,7 +48,7 @@ import PNG_SVG from '../static/download-png.svg';
function groupByStructure(
structures: (Structure | UserStructure)[],
environments?: Environment[]
): Environment[][] | undefined {
): SelectiveEnvironment[][] | undefined {
if (environments === undefined) {
return undefined;
}
Expand All @@ -53,11 +57,15 @@ function groupByStructure(
Array.from({ length: structures[i].size })
);

for (const env of environments) {
result[env.structure][env.center] = env;
for (let i=0; i<environments.length; i++) {
const env = environments[i];
result[env.structure][env.center] = {
environment: i,
...env,
};
}

return result as Environment[][];
return result as SelectiveEnvironment[][];
}

interface ViewerGridData {
Expand Down Expand Up @@ -120,11 +128,11 @@ export class ViewersGrid {
/// List of structures in the dataset
private _structures: Structure[] | UserStructure[];
/// List of properties in the dataset
private _properties: { [name: string]: Property };
private _properties: Record<string, Property>;
Luthaf marked this conversation as resolved.
Show resolved Hide resolved
/// Cached string representation of structures
private _resolvedStructures: Structure[];
/// Optional list of environments for each structure
private _environments?: Environment[][];
private _environments?: SelectiveEnvironment[][];
/// Maximum number of allowed structure viewers
private _maxViewers: number;
/// The indexer translating between environments indexes and structure/atom
Expand Down Expand Up @@ -166,7 +174,11 @@ export class ViewersGrid {
if (properties === undefined) {
this._properties = {};
} else {
this._properties = properties;
const numberProperties = filter(properties, (p) =>
Object.values(p.values).every((v) => typeof v === 'number')
);
const atomProperties = filter(numberProperties, (p) => p.target === 'atom');
this._properties = atomProperties;
}
this._resolvedStructures = new Array<Structure>(structures.length);
this._environments = groupByStructure(this._structures, environments);
Expand Down Expand Up @@ -489,28 +501,19 @@ export class ViewersGrid {
return this._resolvedStructures[index];
}

private _getAtomProperties(): Record<string, Property> {
const numberProperties = filter(this._properties, (p) =>
Object.values(p.values).every((v) => typeof v === 'number')
);
const atomProperties = filter(numberProperties, (p) => p.target === 'atom');
return atomProperties;
}

private _getSelectedAtomProperties(
indexes?: Indexes
structure: number,
): Record<string, (number | undefined)[]> | undefined {
const structureAtomProperties: Record<string, (number | undefined)[]> = {};
const allAtomProperties = this._getAtomProperties();
if (this._environments !== undefined && indexes !== undefined) {
const activeEnvironments = this._environments[indexes.structure];
for (const propertyName in allAtomProperties) {
if (this._environments !== undefined) {
const activeEnvironments = this._environments[structure];
for (const propertyName in this._properties) {
structureAtomProperties[propertyName] = [];
for (const activeEnvironment of activeEnvironments) {
if (activeEnvironment !== undefined) {
structureAtomProperties[propertyName].push(
allAtomProperties[propertyName].values[
activeEnvironment.center
this._properties[propertyName].values[
activeEnvironment.environment
] as number
);
} else {
Expand Down Expand Up @@ -544,7 +547,7 @@ export class ViewersGrid {

viewer.load(
this._structure(indexes.structure),
this._getSelectedAtomProperties(indexes),
this._getSelectedAtomProperties(indexes.structure),
options
);
data.current = indexes;
Expand Down Expand Up @@ -745,10 +748,10 @@ export class ViewersGrid {

// add a new cells if necessary
if (!this._cellsData.has(cellGUID)) {
const propertiesName = this._properties ? Object.keys(this._properties) : [];
const viewer = new MoleculeViewer(
this._getById<HTMLElement>(`gi-${cellGUID}`),
this._indexer,
this._properties
propertiesName
);

viewer.onselect = (atom: number) => {
Expand Down
29 changes: 15 additions & 14 deletions src/structure/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { PositioningCallback, getByID, makeDraggable, sendWarning } from '../uti

import BARS_SVG from '../static/bars.svg';
import HTML_OPTIONS from './options.html.in';
import { MapData, NumericProperties } from '../map/data';

export class StructureOptions extends OptionsGroup {
/// should we show bonds
Expand Down Expand Up @@ -67,7 +66,11 @@ export class StructureOptions extends OptionsGroup {
// Callback to get the initial positioning of the settings modal.
private _positionSettingsModal: PositioningCallback;

constructor(root: HTMLElement, positionSettings: PositioningCallback, properties?: MapData) {
constructor(
root: HTMLElement,
positionSettings: PositioningCallback,
propertiesName?: string[]
Luthaf marked this conversation as resolved.
Show resolved Hide resolved
) {
super();

this.bonds = new HTMLOption('boolean', true);
Expand Down Expand Up @@ -106,8 +109,6 @@ export class StructureOptions extends OptionsGroup {
cutoff: new HTMLOption('number', 4.0),
};

const propertiesName = properties ? Object.keys(properties['atom']) : [];

this.color = {
property: new HTMLOption('string', 'element'),
min: new HTMLOption('number', 0),
Expand All @@ -116,8 +117,12 @@ export class StructureOptions extends OptionsGroup {
palette: new HTMLOption('string', 'Rwb'),
};

// Handling undefined propertiesName:
if (propertiesName === undefined) {
propertiesName = [];
}
Luthaf marked this conversation as resolved.
Show resolved Hide resolved
// validate atom properties for coloring
if (properties && Object.keys(properties['atom']).includes('element')) {
if (propertiesName.includes('element')) {
this.color.property.validate = optionValidator(propertiesName, 'color');
} else {
this.color.property.validate = optionValidator(
Expand Down Expand Up @@ -151,11 +156,7 @@ export class StructureOptions extends OptionsGroup {
).adoptedStyleSheets;
this._openModal = openModal;
root.appendChild(this._openModal);
if (properties) {
this._bind(properties['atom']);
} else {
this._bind({});
}
this._bind(propertiesName);
}

/** Get in a element in the modal from its id */
Expand Down Expand Up @@ -263,7 +264,7 @@ export class StructureOptions extends OptionsGroup {
}

/** Bind all options to the corresponding HTML elements */
private _bind(atom: NumericProperties): void {
private _bind(propertiesName: string[]): void {
this.atomLabels.bind(this.getModalElement('atom-labels'), 'checked');

const selectShape = this.getModalElement<HTMLSelectElement>('shapes');
Expand All @@ -284,11 +285,11 @@ export class StructureOptions extends OptionsGroup {
const selectColorProperty = this.getModalElement<HTMLSelectElement>('atom-color-property');
// first option is 'element'
selectColorProperty.options.length = 0;
if (!Object.keys(atom).includes('element')) {
if (!propertiesName.includes('element')) {
selectColorProperty.options.add(new Option('element', 'element'));
}
for (const key in atom) {
selectColorProperty.options.add(new Option(key, key));
for (const property of propertiesName) {
selectColorProperty.options.add(new Option(property, property));
}
this.color.property.bind(selectColorProperty, 'value');
this.color.mode.bind(this.getModalElement('atom-color-transform'), 'value');
Expand Down
Loading