giagitom 1 год назад
Родитель
Сommit
db83b97ff9

+ 13 - 17
src/mol-geo/geometry/color-data.ts

@@ -3,6 +3,7 @@
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { ValueCell } from '../../mol-util';
@@ -11,7 +12,6 @@ import { Color } from '../../mol-util/color';
 import { Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
 import { LocationIterator } from '../util/location-iterator';
 import { NullLocation } from '../../mol-model/location';
-import { StructureElement, Bond } from '../../mol-model/structure';
 import { LocationColor, ColorTheme, ColorVolume } from '../../mol-theme/color';
 import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
 
@@ -156,33 +156,29 @@ function createInstanceColor(locationIt: LocationIterator, color: LocationColor,
 
 /** Creates color texture with color for each group (i.e. shared across instances) */
 function createGroupColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
-    const { groupCount } = locationIt;
-    const colors = createTextureImage(Math.max(1, groupCount * 2), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
+    const { groupCount, hasLocation2 } = locationIt;
+    const colors = createTextureImage(Math.max(1, groupCount * (hasLocation2 ? 2 : 1)), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
     locationIt.reset();
-    const bLoc = StructureElement.Location.create();
+    const indexMultiplier = hasLocation2 ? 6 : 3;
     while (locationIt.hasNext && !locationIt.isNextNewInstance) {
-        const { location, isSecondary, groupIndex } = locationIt.move();
-        Color.toArray(color(location, isSecondary), colors.array, groupIndex * 3 * 2);
-        if (Bond.isLocation(location)) {
-            const { bStructure, bUnit, bIndex } = location;
-            bLoc.structure = bStructure;
-            bLoc.unit = bUnit;
-            bLoc.element = bUnit.elements[bIndex];
-            Color.toArray(color(bLoc, isSecondary), colors.array, groupIndex * 3 * 2 + 3);
-        }
+        const { location, location2, isSecondary, groupIndex } = locationIt.move();
+        Color.toArray(color(location, isSecondary), colors.array, groupIndex * indexMultiplier);
+        if (hasLocation2) Color.toArray(color(location2, isSecondary), colors.array, groupIndex * indexMultiplier + 3);
     }
     return createTextureColor(colors, 'group', colorData);
 }
 
 /** Creates color texture with color for each group in each instance */
 function createGroupInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
-    const { groupCount, instanceCount } = locationIt;
-    const count = instanceCount * groupCount;
+    const { groupCount, instanceCount, hasLocation2 } = locationIt;
+    const count = instanceCount * groupCount * (hasLocation2 ? 2 : 1);
     const colors = createTextureImage(Math.max(1, count), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
     locationIt.reset();
+    const indexMultiplier = hasLocation2 ? 6 : 3;
     while (locationIt.hasNext) {
-        const { location, isSecondary, index } = locationIt.move();
-        Color.toArray(color(location, isSecondary), colors.array, index * 3);
+        const { location, location2, isSecondary, index } = locationIt.move();
+        Color.toArray(color(location, isSecondary), colors.array, index * indexMultiplier);
+        if (hasLocation2) Color.toArray(color(location2, isSecondary), colors.array, index * indexMultiplier + 3);
     }
     return createTextureColor(colors, 'groupInstance', colorData);
 }

+ 4 - 1
src/mol-geo/geometry/cylinders/cylinders.ts

@@ -2,6 +2,7 @@
  * Copyright (c) 2020-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { ValueCell } from '../../../mol-util';
@@ -166,6 +167,7 @@ export namespace Cylinders {
         solidInterior: PD.Boolean(true, BaseGeometry.ShadingCategory),
         bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
         bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
+        colorMode: PD.Select('default', PD.arrayToOptions(['default', 'interpolate']), BaseGeometry.ShadingCategory)
     };
     export type Params = typeof Params
 
@@ -220,7 +222,6 @@ export namespace Cylinders {
         const padding = getMaxSize(size) * props.sizeFactor;
         const invariantBoundingSphere = Sphere3D.clone(cylinders.boundingSphere);
         const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount, 0);
-
         return {
             dGeometryType: ValueCell.create('cylinders'),
 
@@ -255,6 +256,7 @@ export namespace Cylinders {
             dSolidInterior: ValueCell.create(props.solidInterior),
             uBumpFrequency: ValueCell.create(props.bumpFrequency),
             uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
+            dDualColor: ValueCell.create(props.colorMode === 'interpolate'),
         };
     }
 
@@ -274,6 +276,7 @@ export namespace Cylinders {
         ValueCell.updateIfChanged(values.dSolidInterior, props.solidInterior);
         ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
         ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
+        ValueCell.updateIfChanged(values.dDualColor, props.colorMode === 'interpolate');
     }
 
     function updateBoundingSphere(values: CylindersValues, cylinders: Cylinders) {

+ 10 - 1
src/mol-geo/util/location-iterator.ts

@@ -2,6 +2,7 @@
  * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { Iterator } from '../../mol-data';
@@ -10,6 +11,7 @@ import { NullLocation, Location } from '../../mol-model/location';
 
 export interface LocationValue {
     location: Location
+    location2: Location
     index: number
     groupIndex: number
     instanceIndex: number
@@ -24,6 +26,7 @@ export interface LocationIterator extends Iterator<LocationValue> {
     readonly count: number
     readonly stride: number
     readonly nonInstanceable: boolean
+    readonly hasLocation2: boolean
     move(): LocationValue
     reset(): void
     skipInstance(): void
@@ -33,13 +36,14 @@ export interface LocationIterator extends Iterator<LocationValue> {
 type LocationGetter = (groupIndex: number, instanceIndex: number) => Location
 type IsSecondaryGetter = (groupIndex: number, instanceIndex: number) => boolean
 
-export function LocationIterator(groupCount: number, instanceCount: number, stride: number, getLocation: LocationGetter, nonInstanceable = false, isSecondary: IsSecondaryGetter = () => false): LocationIterator {
+export function LocationIterator(groupCount: number, instanceCount: number, stride: number, getLocation: LocationGetter, nonInstanceable = false, isSecondary: IsSecondaryGetter = () => false, getLocation2?: LocationGetter): LocationIterator {
     if (groupCount % stride !== 0) {
         throw new Error('incompatible groupCount and stride');
     }
 
     const value: LocationValue = {
         location: NullLocation as Location,
+        location2: NullLocation as Location,
         index: 0,
         groupIndex: 0,
         instanceIndex: 0,
@@ -52,6 +56,8 @@ export function LocationIterator(groupCount: number, instanceCount: number, stri
     let instanceIndex = 0;
     let voidInstances = false;
 
+    const hasLocation2 = !!getLocation2;
+
     return {
         get hasNext() { return hasNext; },
         get isNextNewInstance() { return isNextNewInstance; },
@@ -60,12 +66,14 @@ export function LocationIterator(groupCount: number, instanceCount: number, stri
         count: groupCount * instanceCount,
         stride,
         nonInstanceable,
+        hasLocation2,
         move() {
             if (hasNext) {
                 value.groupIndex = groupIndex;
                 value.instanceIndex = instanceIndex;
                 value.index = instanceIndex * groupCount + groupIndex;
                 value.location = getLocation(groupIndex, voidInstances ? -1 : instanceIndex);
+                if (hasLocation2) value.location2 = getLocation2(groupIndex, voidInstances ? -1 : instanceIndex);
                 value.isSecondary = isSecondary(groupIndex, voidInstances ? -1 : instanceIndex);
                 groupIndex += stride;
                 if (groupIndex === groupCount) {
@@ -81,6 +89,7 @@ export function LocationIterator(groupCount: number, instanceCount: number, stri
         },
         reset() {
             value.location = NullLocation;
+            value.location2 = NullLocation;
             value.index = 0;
             value.groupIndex = 0;
             value.instanceIndex = 0;

+ 1 - 0
src/mol-gl/renderable/cylinders.ts

@@ -31,6 +31,7 @@ export const CylindersSchema = {
     dSolidInterior: DefineSpec('boolean'),
     uBumpFrequency: UniformSpec('f', 'material'),
     uBumpAmplitude: UniformSpec('f', 'material'),
+    dDualColor: DefineSpec('boolean'),
 };
 export type CylindersSchema = typeof CylindersSchema
 export type CylindersValues = Values<CylindersSchema>

+ 11 - 3
src/mol-gl/shader/chunks/assign-color-varying.glsl.ts

@@ -5,12 +5,20 @@ export const assign_color_varying = `
     #elif defined(dColorType_instance)
         vColor.rgb = readFromTexture(tColor, aInstance, uColorTexDim).rgb;
     #elif defined(dColorType_group)
-        vColor.rgb = readFromTexture(tColor, group * 2.0, uColorTexDim).rgb;
-        #if defined(multiColor)
+        #if defined(dDualColor)       
+            vColor.rgb = readFromTexture(tColor, group * 2.0, uColorTexDim).rgb;
             vColor2.rgb = readFromTexture(tColor, group * 2.0 + 1.0, uColorTexDim).rgb;
+        #else
+            vColor.rgb = readFromTexture(tColor, group, uColorTexDim).rgb;
         #endif
     #elif defined(dColorType_groupInstance)
-        vColor.rgb = readFromTexture(tColor, aInstance * float(uGroupCount) + group, uColorTexDim).rgb;
+        #if defined(dDualColor)       
+            vColor.rgb = readFromTexture(tColor, (aInstance * float(uGroupCount) + group) * 2.0, uColorTexDim).rgb;
+            vColor2.rgb = readFromTexture(tColor, (aInstance * float(uGroupCount) + group) * 2.0 + 1.0, uColorTexDim).rgb;
+        #else
+            vColor.rgb = readFromTexture(tColor, aInstance * float(uGroupCount) + group, uColorTexDim).rgb;
+        #endif
+        
     #elif defined(dColorType_vertex)
         vColor.rgb = readFromTexture(tColor, VertexID, uColorTexDim).rgb;
     #elif defined(dColorType_vertexInstance)

+ 1 - 2
src/mol-gl/shader/chunks/assign-material-color.glsl.ts

@@ -13,10 +13,9 @@ export const assign_material_color = `
         vec4 material = vec4(uColor, uAlpha);
     #elif defined(dColorType_varying)
         vec4 material = vec4(vColor.rgb, uAlpha);
-        #if defined(multiColor)
+        #if defined(dDualColor)
             //interpolate
             if (vColorMode >= 0.0 && vColorMode <= 1.0){
-                //material = vec4(mix(mix(vColor.rgb, vColor2.rgb, vColorMode - 0.1).rgb, mix(vColor2.rgb, vColor.rgb, 1.0 - vColorMode).rgb, distance(vStart,vModelPosition) / (distance(vStart,vEnd))).rgb, uAlpha);
                 material = vec4(mix(vColor.rgb, vColor2.rgb, vColorMode).rgb, uAlpha);
             } else if (vColorMode > 2.9 && vColorMode < 3.1) {
                 vec3 b = vEnd - vStart;

+ 1 - 1
src/mol-gl/shader/chunks/color-frag-params.glsl.ts

@@ -12,7 +12,7 @@ uniform float uBumpiness;
         uniform vec3 uColor;
     #elif defined(dColorType_varying)
         varying vec4 vColor;
-        #ifdef multiColor
+        #ifdef dDualColor
           varying vec4 vColor2;
         #endif
     #endif

+ 1 - 1
src/mol-gl/shader/chunks/color-vert-params.glsl.ts

@@ -11,7 +11,7 @@ uniform float uBumpiness;
         attribute vec3 aColor;
     #elif defined(dColorType_texture)
         varying vec4 vColor;
-        #ifdef multiColor
+        #ifdef dDualColor
           varying vec4 vColor2;
         #endif
         uniform vec2 uColorTexDim;

+ 0 - 1
src/mol-gl/shader/cylinders.frag.ts

@@ -9,7 +9,6 @@ precision highp float;
 precision highp int;
 
 #define bumpEnabled
-#define multiColor
 
 uniform mat4 uView;
 

+ 0 - 2
src/mol-gl/shader/cylinders.vert.ts

@@ -8,8 +8,6 @@ export const cylinders_vert = `
 precision highp float;
 precision highp int;
 
-#define multiColor
-
 #include common
 #include read_from_texture
 #include common_vert_params

+ 4 - 3
src/mol-repr/structure/complex-visual.ts

@@ -2,6 +2,7 @@
  * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
@@ -52,7 +53,7 @@ function createComplexRenderObject<G extends Geometry>(structure: Structure, geo
 interface ComplexVisualBuilder<P extends StructureParams, G extends Geometry> {
     defaultProps: PD.Values<P>
     createGeometry(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<P>, geometry?: G): Promise<G> | G
-    createLocationIterator(structure: Structure): LocationIterator
+    createLocationIterator(structure: Structure, props: PD.Values<P>): LocationIterator
     getLoci(pickingId: PickingId, structure: Structure, id: number): Loci
     eachLocation(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean, isMarking: boolean): boolean,
     setUpdateState(state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme, newStructure: Structure, currentStructure: Structure): void
@@ -143,7 +144,7 @@ export function ComplexVisual<G extends Geometry, P extends StructureParams & Ge
 
     function update(newGeometry?: G) {
         if (updateState.createNew) {
-            locationIt = createLocationIterator(newStructure);
+            locationIt = createLocationIterator(newStructure, newProps);
             if (newGeometry) {
                 renderObject = createComplexRenderObject(newStructure, newGeometry, locationIt, newTheme, newProps, materialId);
                 positionIt = createPositionIterator(newGeometry, renderObject.values);
@@ -157,7 +158,7 @@ export function ComplexVisual<G extends Geometry, P extends StructureParams & Ge
 
             if (updateState.updateTransform) {
                 // console.log('update transform')
-                locationIt = createLocationIterator(newStructure);
+                locationIt = createLocationIterator(newStructure, newProps);
                 const { instanceCount, groupCount } = locationIt;
                 if (newProps.instanceGranularity) {
                     createMarkers(instanceCount, 'instance', renderObject.values);

+ 4 - 3
src/mol-repr/structure/units-visual.ts

@@ -2,6 +2,7 @@
  * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
@@ -55,7 +56,7 @@ function createUnitsRenderObject<G extends Geometry>(structureGroup: StructureGr
 interface UnitsVisualBuilder<P extends StructureParams, G extends Geometry> {
     defaultProps: PD.Values<P>
     createGeometry(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<P>, geometry?: G): Promise<G> | G
-    createLocationIterator(structureGroup: StructureGroup): LocationIterator
+    createLocationIterator(structureGroup: StructureGroup, props: PD.Values<P>): LocationIterator
     getLoci(pickingId: PickingId, structureGroup: StructureGroup, id: number): Loci
     eachLocation(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean, isMarking: boolean): boolean
     setUpdateState(state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme, newStructureGroup: StructureGroup, currentStructureGroup: StructureGroup): void
@@ -182,7 +183,7 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
 
     function update(newGeometry?: G) {
         if (updateState.createNew) {
-            locationIt = createLocationIterator(newStructureGroup);
+            locationIt = createLocationIterator(newStructureGroup, newProps);
             if (newGeometry) {
                 renderObject = createUnitsRenderObject(newStructureGroup, newGeometry, locationIt, newTheme, newProps, materialId);
                 positionIt = createPositionIterator(newGeometry, renderObject.values);
@@ -196,7 +197,7 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
 
             if (updateState.updateTransform) {
                 // console.log('update transform');
-                locationIt = createLocationIterator(newStructureGroup);
+                locationIt = createLocationIterator(newStructureGroup, newProps);
                 const { instanceCount, groupCount } = locationIt;
                 if (newProps.instanceGranularity) {
                     createMarkers(instanceCount, 'instance', renderObject.values);

+ 8 - 3
src/mol-repr/structure/visual/bond-inter-unit-cylinder.ts

@@ -2,6 +2,7 @@
  * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
@@ -213,7 +214,7 @@ export function InterUnitBondCylinderImpostorVisual(materialId: number): Complex
     return ComplexCylindersVisual<InterUnitBondCylinderParams>({
         defaultProps: PD.getDefaultValues(InterUnitBondCylinderParams),
         createGeometry: createInterUnitBondCylinderImpostors,
-        createLocationIterator: BondIterator.fromStructure,
+        createLocationIterator: BondIterator.fromStructureLoc2,
         getLoci: getInterBondLoci,
         eachLocation: eachInterBond,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<InterUnitBondCylinderParams>, currentProps: PD.Values<InterUnitBondCylinderParams>, newTheme: Theme, currentTheme: Theme, newStructure: Structure, currentStructure: Structure) => {
@@ -235,9 +236,13 @@ export function InterUnitBondCylinderImpostorVisual(materialId: number): Complex
                 !arrayEqual(newProps.includeTypes, currentProps.includeTypes) ||
                 !arrayEqual(newProps.excludeTypes, currentProps.excludeTypes) ||
                 newProps.adjustCylinderLength !== currentProps.adjustCylinderLength ||
-                newProps.multipleBonds !== currentProps.multipleBonds ||
-                newProps.colorMode !== currentProps.colorMode
+                newProps.multipleBonds !== currentProps.multipleBonds
             );
+            if (newProps.colorMode !== currentProps.colorMode) {
+                state.createGeometry = true;
+                state.updateTransform = true;
+                state.updateColor = true;
+            }
 
             if (newStructure.interUnitBonds !== currentStructure.interUnitBonds) {
                 state.createGeometry = true;

+ 9 - 3
src/mol-repr/structure/visual/bond-intra-unit-cylinder.ts

@@ -3,6 +3,7 @@
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
@@ -230,7 +231,7 @@ export function IntraUnitBondCylinderImpostorVisual(materialId: number): UnitsVi
     return UnitsCylindersVisual<IntraUnitBondCylinderParams>({
         defaultProps: PD.getDefaultValues(IntraUnitBondCylinderParams),
         createGeometry: createIntraUnitBondCylinderImpostors,
-        createLocationIterator: BondIterator.fromGroup,
+        createLocationIterator: BondIterator.fromGroupLoc2,
         getLoci: getIntraBondLoci,
         eachLocation: eachIntraBond,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<IntraUnitBondCylinderParams>, currentProps: PD.Values<IntraUnitBondCylinderParams>, newTheme: Theme, currentTheme: Theme, newStructureGroup: StructureGroup, currentStructureGroup: StructureGroup) => {
@@ -253,10 +254,15 @@ export function IntraUnitBondCylinderImpostorVisual(materialId: number): UnitsVi
                 !arrayEqual(newProps.excludeTypes, currentProps.excludeTypes) ||
                 newProps.adjustCylinderLength !== currentProps.adjustCylinderLength ||
                 newProps.aromaticBonds !== currentProps.aromaticBonds ||
-                newProps.multipleBonds !== currentProps.multipleBonds ||
-                newProps.colorMode !== currentProps.colorMode
+                newProps.multipleBonds !== currentProps.multipleBonds
             );
 
+            if (newProps.colorMode !== currentProps.colorMode) {
+                state.createGeometry = true;
+                state.updateTransform = true;
+                state.updateColor = true;
+            }
+
             const newUnit = newStructureGroup.group.units[0];
             const currentUnit = currentStructureGroup.group.units[0];
             if (Unit.isAtomic(newUnit) && Unit.isAtomic(currentUnit)) {

+ 57 - 0
src/mol-repr/structure/visual/util/bond.ts

@@ -3,6 +3,7 @@
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { BondType } from '../../../../mol-model/structure/model/types';
@@ -142,6 +143,35 @@ export namespace BondIterator {
         return LocationIterator(groupCount, instanceCount, 1, getLocation);
     }
 
+    export function fromGroupLoc2(structureGroup: StructureGroup, props: PD.Values): LocationIterator {
+        const { group, structure } = structureGroup;
+        const unit = group.units[0] as Unit.Atomic;
+        const groupCount = Unit.isAtomic(unit) ? unit.bonds.edgeCount * 2 : 0;
+        const instanceCount = group.units.length;
+        const location = Bond.Location(structure, undefined, undefined, structure, undefined, undefined);
+        const getLocation = (groupIndex: number, instanceIndex: number) => {
+            const unit = group.units[instanceIndex] as Unit.Atomic;
+            location.aUnit = unit;
+            location.bUnit = unit;
+            location.aIndex = unit.bonds.a[groupIndex];
+            location.bIndex = unit.bonds.b[groupIndex];
+            return location;
+        };
+        if (props.colorMode === 'interpolate') {
+            const location2 = Bond.Location(structure, undefined, undefined, structure, undefined, undefined);
+            const getLocation2 = (groupIndex: number, instanceIndex: number) => { // inverting A with B
+                const unit = group.units[instanceIndex] as Unit.Atomic;
+                location2.aUnit = unit;
+                location2.bUnit = unit;
+                location2.aIndex = unit.bonds.b[groupIndex];
+                location2.bIndex = unit.bonds.a[groupIndex];
+                return location2;
+            };
+            return LocationIterator(groupCount, instanceCount, 1, getLocation, false, () => false, getLocation2);
+        }
+        return LocationIterator(groupCount, instanceCount, 1, getLocation);
+    }
+
     export function fromStructure(structure: Structure): LocationIterator {
         const groupCount = structure.interUnitBonds.edgeCount;
         const instanceCount = 1;
@@ -156,6 +186,33 @@ export namespace BondIterator {
         };
         return LocationIterator(groupCount, instanceCount, 1, getLocation, true);
     }
+
+    export function fromStructureLoc2(structure: Structure, props: PD.Values): LocationIterator {
+        const groupCount = structure.interUnitBonds.edgeCount;
+        const instanceCount = 1;
+        const location = Bond.Location(structure, undefined, undefined, structure, undefined, undefined);
+        const getLocation = (groupIndex: number) => {
+            const bond = structure.interUnitBonds.edges[groupIndex];
+            location.aUnit = structure.unitMap.get(bond.unitA);
+            location.aIndex = bond.indexA;
+            location.bUnit = structure.unitMap.get(bond.unitB);
+            location.bIndex = bond.indexB;
+            return location;
+        };
+        if (props.colorMode === 'interpolate') {
+            const location2 = Bond.Location(structure, undefined, undefined, structure, undefined, undefined);
+            const getLocation2 = (groupIndex: number) => { // inverting A with B
+                const bond = structure.interUnitBonds.edges[groupIndex];
+                location2.aUnit = structure.unitMap.get(bond.unitB);
+                location2.aIndex = bond.indexB;
+                location2.bUnit = structure.unitMap.get(bond.unitA);
+                location2.bIndex = bond.indexA;
+                return location2;
+            };
+            return LocationIterator(groupCount, instanceCount, 1, getLocation, true, () => false, getLocation2);
+        };
+        return LocationIterator(groupCount, instanceCount, 1, getLocation, true);
+    }
 }
 
 //