Browse Source

Implement 2 colors interpolation on impostor cylinders

giagitom 2 năm trước cách đây
mục cha
commit
c59ae908b8

+ 12 - 3
src/mol-geo/geometry/color-data.ts

@@ -11,6 +11,7 @@ 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,11 +157,19 @@ 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), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
+    const colors = createTextureImage(Math.max(1, groupCount * 2), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
     locationIt.reset();
+    const bLoc = StructureElement.Location.create();
     while (locationIt.hasNext && !locationIt.isNextNewInstance) {
         const { location, isSecondary, groupIndex } = locationIt.move();
-        Color.toArray(color(location, isSecondary), colors.array, groupIndex * 3);
+        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);
+        }
     }
     return createTextureColor(colors, 'group', colorData);
 }
@@ -258,4 +267,4 @@ function createDirectColor(colorData?: ColorData): ColorData {
             dUsePalette: ValueCell.create(false),
         };
     }
-}
+}

+ 16 - 9
src/mol-geo/geometry/cylinders/cylinders-builder.ts

@@ -9,9 +9,9 @@ import { Cylinders } from './cylinders';
 import { Vec3 } from '../../../mol-math/linear-algebra';
 
 export interface CylindersBuilder {
-    add(startX: number, startY: number, startZ: number, endX: number, endY: number, endZ: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number): void
-    addFixedCountDashes(start: Vec3, end: Vec3, segmentCount: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number): void
-    addFixedLengthDashes(start: Vec3, end: Vec3, segmentLength: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number): void
+    add(startX: number, startY: number, startZ: number, endX: number, endY: number, endZ: number, radiusScale: number, topCap: boolean, bottomCap: boolean, colorMode: number, group: number): void
+    addFixedCountDashes(start: Vec3, end: Vec3, segmentCount: number, radiusScale: number, topCap: boolean, bottomCap: boolean, interpolate: boolean, group: number): void
+    addFixedLengthDashes(start: Vec3, end: Vec3, segmentLength: number, radiusScale: number, topCap: boolean, bottomCap: boolean, interpolate: boolean, group: number): void
     getCylinders(): Cylinders
 }
 
@@ -30,21 +30,24 @@ export namespace CylindersBuilder {
         const ends = ChunkedArray.create(Float32Array, 3, chunkSize, cylinders ? cylinders.endBuffer.ref.value : initialCount);
         const scales = ChunkedArray.create(Float32Array, 1, chunkSize, cylinders ? cylinders.scaleBuffer.ref.value : initialCount);
         const caps = ChunkedArray.create(Float32Array, 1, chunkSize, cylinders ? cylinders.capBuffer.ref.value : initialCount);
+        const colorModes = ChunkedArray.create(Float32Array, 1, chunkSize, cylinders ? cylinders.colorModeBuffer.ref.value : initialCount);
 
-        const add = (startX: number, startY: number, startZ: number, endX: number, endY: number, endZ: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number) => {
+        const add = (startX: number, startY: number, startZ: number, endX: number, endY: number, endZ: number, radiusScale: number, topCap: boolean, bottomCap: boolean, colorMode: number, group: number) => {
             for (let i = 0; i < 6; ++i) {
                 caAdd3(starts, startX, startY, startZ);
                 caAdd3(ends, endX, endY, endZ);
                 caAdd(groups, group);
                 caAdd(scales, radiusScale);
                 caAdd(caps, (topCap ? 1 : 0) + (bottomCap ? 2 : 0));
+                caAdd(colorModes, colorMode);
             }
         };
 
-        const addFixedCountDashes = (start: Vec3, end: Vec3, segmentCount: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number) => {
+        const addFixedCountDashes = (start: Vec3, end: Vec3, segmentCount: number, radiusScale: number, topCap: boolean, bottomCap: boolean, interpolate: boolean, group: number) => {
             const d = Vec3.distance(start, end);
             const s = Math.floor(segmentCount / 2);
             const step = 1 / segmentCount;
+            let colorMode = 2.0;
 
             Vec3.sub(tmpDir, end, start);
             for (let j = 0; j < s; ++j) {
@@ -53,16 +56,19 @@ export namespace CylindersBuilder {
                 Vec3.add(tmpVecA, start, tmpDir);
                 Vec3.setMagnitude(tmpDir, tmpDir, d * step * ((j + 1) * 2));
                 Vec3.add(tmpVecB, start, tmpDir);
-                add(tmpVecA[0], tmpVecA[1], tmpVecA[2], tmpVecB[0], tmpVecB[1], tmpVecB[2], radiusScale, topCap, bottomCap, group);
+                if (interpolate) {
+                    colorMode = Vec3.distance(start, tmpVecB) / (d * 2);
+                }
+                add(tmpVecA[0], tmpVecA[1], tmpVecA[2], tmpVecB[0], tmpVecB[1], tmpVecB[2], radiusScale, topCap, bottomCap, colorMode, group);
             }
         };
 
         return {
             add,
             addFixedCountDashes,
-            addFixedLengthDashes: (start: Vec3, end: Vec3, segmentLength: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number) => {
+            addFixedLengthDashes: (start: Vec3, end: Vec3, segmentLength: number, radiusScale: number, topCap: boolean, bottomCap: boolean, interpolate: boolean, group: number) => {
                 const d = Vec3.distance(start, end);
-                addFixedCountDashes(start, end, d / segmentLength, radiusScale, topCap, bottomCap, group);
+                addFixedCountDashes(start, end, d / segmentLength, radiusScale, topCap, bottomCap, interpolate, group);
             },
             getCylinders: () => {
                 const cylinderCount = groups.elementCount / 6;
@@ -71,10 +77,11 @@ export namespace CylindersBuilder {
                 const eb = ChunkedArray.compact(ends, true) as Float32Array;
                 const ab = ChunkedArray.compact(scales, true) as Float32Array;
                 const cb = ChunkedArray.compact(caps, true) as Float32Array;
+                const cmb = ChunkedArray.compact(colorModes, true) as Float32Array;
                 const mb = cylinders && cylinderCount <= cylinders.cylinderCount ? cylinders.mappingBuffer.ref.value : new Float32Array(cylinderCount * 18);
                 const ib = cylinders && cylinderCount <= cylinders.cylinderCount ? cylinders.indexBuffer.ref.value : new Uint32Array(cylinderCount * 12);
                 if (!cylinders || cylinderCount > cylinders.cylinderCount) fillMappingAndIndices(cylinderCount, mb, ib);
-                return Cylinders.create(mb, ib, gb, sb, eb, ab, cb, cylinderCount, cylinders);
+                return Cylinders.create(mb, ib, gb, sb, eb, ab, cb, cmb, cylinderCount, cylinders);
             }
         };
     }

+ 14 - 8
src/mol-geo/geometry/cylinders/cylinders.ts

@@ -47,6 +47,8 @@ export interface Cylinders {
     readonly scaleBuffer: ValueCell<Float32Array>,
     /** Cylinder cap buffer as array of cap flags wrapped in a value cell */
     readonly capBuffer: ValueCell<Float32Array>,
+    /** Cylinder colorMode buffer as array of coloring modes flags wrapped in a value cell */
+    readonly colorModeBuffer: ValueCell<Float32Array>,
 
     /** Bounding sphere of the cylinders */
     readonly boundingSphere: Sphere3D
@@ -57,10 +59,10 @@ export interface Cylinders {
 }
 
 export namespace Cylinders {
-    export function create(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, scales: Float32Array, caps: Float32Array, cylinderCount: number, cylinders?: Cylinders): Cylinders {
+    export function create(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, scales: Float32Array, caps: Float32Array, colorModes: Float32Array, cylinderCount: number, cylinders?: Cylinders): Cylinders {
         return cylinders ?
-            update(mappings, indices, groups, starts, ends, scales, caps, cylinderCount, cylinders) :
-            fromArrays(mappings, indices, groups, starts, ends, scales, caps, cylinderCount);
+            update(mappings, indices, groups, starts, ends, scales, caps, colorModes, cylinderCount, cylinders) :
+            fromArrays(mappings, indices, groups, starts, ends, scales, caps, colorModes, cylinderCount);
     }
 
     export function createEmpty(cylinders?: Cylinders): Cylinders {
@@ -71,17 +73,18 @@ export namespace Cylinders {
         const eb = cylinders ? cylinders.endBuffer.ref.value : new Float32Array(0);
         const ab = cylinders ? cylinders.scaleBuffer.ref.value : new Float32Array(0);
         const cb = cylinders ? cylinders.capBuffer.ref.value : new Float32Array(0);
-        return create(mb, ib, gb, sb, eb, ab, cb, 0, cylinders);
+        const cmb = cylinders ? cylinders.colorModeBuffer.ref.value : new Float32Array(0);
+        return create(mb, ib, gb, sb, eb, ab, cb, cmb, 0, cylinders);
     }
 
     function hashCode(cylinders: Cylinders) {
         return hashFnv32a([
             cylinders.cylinderCount, cylinders.mappingBuffer.ref.version, cylinders.indexBuffer.ref.version,
-            cylinders.groupBuffer.ref.version, cylinders.startBuffer.ref.version, cylinders.endBuffer.ref.version, cylinders.scaleBuffer.ref.version, cylinders.capBuffer.ref.version
+            cylinders.groupBuffer.ref.version, cylinders.startBuffer.ref.version, cylinders.endBuffer.ref.version, cylinders.scaleBuffer.ref.version, cylinders.capBuffer.ref.version, cylinders.colorModeBuffer.ref.version
         ]);
     }
 
-    function fromArrays(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, scales: Float32Array, caps: Float32Array, cylinderCount: number): Cylinders {
+    function fromArrays(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, scales: Float32Array, caps: Float32Array, colorModes: Float32Array, cylinderCount: number): Cylinders {
 
         const boundingSphere = Sphere3D();
         let groupMapping: GroupMapping;
@@ -99,6 +102,7 @@ export namespace Cylinders {
             endBuffer: ValueCell.create(ends),
             scaleBuffer: ValueCell.create(scales),
             capBuffer: ValueCell.create(caps),
+            colorModeBuffer: ValueCell.create(colorModes),
             get boundingSphere() {
                 const newHash = hashCode(cylinders);
                 if (newHash !== currentHash) {
@@ -125,7 +129,7 @@ export namespace Cylinders {
         return cylinders;
     }
 
-    function update(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, scales: Float32Array, caps: Float32Array, cylinderCount: number, cylinders: Cylinders) {
+    function update(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, scales: Float32Array, caps: Float32Array, colorModes: Float32Array, cylinderCount: number, cylinders: Cylinders) {
         if (cylinderCount > cylinders.cylinderCount) {
             ValueCell.update(cylinders.mappingBuffer, mappings);
             ValueCell.update(cylinders.indexBuffer, indices);
@@ -136,6 +140,7 @@ export namespace Cylinders {
         ValueCell.update(cylinders.endBuffer, ends);
         ValueCell.update(cylinders.scaleBuffer, scales);
         ValueCell.update(cylinders.capBuffer, caps);
+        ValueCell.update(cylinders.colorModeBuffer, colorModes);
         return cylinders;
     }
 
@@ -225,6 +230,7 @@ export namespace Cylinders {
             aEnd: cylinders.endBuffer,
             aScale: cylinders.scaleBuffer,
             aCap: cylinders.capBuffer,
+            aColorMode: cylinders.colorModeBuffer,
             elements: cylinders.indexBuffer,
             boundingSphere: ValueCell.create(boundingSphere),
             invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
@@ -294,4 +300,4 @@ export namespace Cylinders {
         state.opaque = state.opaque && !props.xrayShaded;
         state.writeDepth = state.opaque;
     }
-}
+}

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

@@ -20,6 +20,7 @@ export const CylindersSchema = {
     aMapping: AttributeSpec('float32', 3, 0),
     aScale: AttributeSpec('float32', 1, 0),
     aCap: AttributeSpec('float32', 1, 0),
+    aColorMode: AttributeSpec('float32', 1, 0),
     elements: ElementsSpec('uint32'),
 
     padding: ValueSpec('number'),

+ 5 - 2
src/mol-gl/shader/chunks/assign-color-varying.glsl.ts

@@ -5,7 +5,10 @@ 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, uColorTexDim).rgb;
+        vColor.rgb = readFromTexture(tColor, group * 2.0, uColorTexDim).rgb;
+        #if defined(multiColor)
+            vColor2.rgb = readFromTexture(tColor, group * 2.0 + 1.0, uColorTexDim).rgb;
+        #endif
     #elif defined(dColorType_groupInstance)
         vColor.rgb = readFromTexture(tColor, aInstance * float(uGroupCount) + group, uColorTexDim).rgb;
     #elif defined(dColorType_vertex)
@@ -90,4 +93,4 @@ export const assign_color_varying = `
     #endif
     vTransparency *= uTransparencyStrength;
 #endif
-`;
+`;

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

@@ -13,6 +13,18 @@ export const assign_material_color = `
         vec4 material = vec4(uColor, uAlpha);
     #elif defined(dColorType_varying)
         vec4 material = vec4(vColor.rgb, uAlpha);
+        #if defined(multiColor)
+            //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;
+                vec3 a = vModelPosition - vStart;
+                vec3 projection = dot(a,b) / dot(b,b) * b;
+                material = vec4(mix(vColor.rgb, vColor2.rgb, dot(projection,projection) / dot(b,b) / 2.0).rgb, uAlpha);
+            }
+        #endif
     #endif
 
     // mix material with overpaint
@@ -121,4 +133,4 @@ export const assign_material_color = `
         #endif
     #endif
 #endif
-`;
+`;

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

@@ -12,6 +12,9 @@ uniform float uBumpiness;
         uniform vec3 uColor;
     #elif defined(dColorType_varying)
         varying vec4 vColor;
+        #ifdef multiColor
+          varying vec4 vColor2;
+        #endif
     #endif
 
     #ifdef dUsePalette
@@ -49,4 +52,4 @@ uniform float uBumpiness;
 #ifdef dTransparency
     varying float vTransparency;
 #endif
-`;
+`;

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

@@ -11,6 +11,9 @@ uniform float uBumpiness;
         attribute vec3 aColor;
     #elif defined(dColorType_texture)
         varying vec4 vColor;
+        #ifdef multiColor
+          varying vec4 vColor2;
+        #endif
         uniform vec2 uColorTexDim;
         uniform sampler2D tColor;
     #elif defined(dColorType_grid)
@@ -90,4 +93,4 @@ uniform float uBumpiness;
     #endif
     uniform float uTransparencyStrength;
 #endif
-`;
+`;

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

@@ -9,6 +9,7 @@ precision highp float;
 precision highp int;
 
 #define bumpEnabled
+#define multiColor
 
 uniform mat4 uView;
 
@@ -17,6 +18,7 @@ varying vec3 vStart;
 varying vec3 vEnd;
 varying float vSize;
 varying float vCap;
+varying float vColorMode;
 
 uniform vec3 uCameraDir;
 uniform vec3 uCameraPosition;

+ 6 - 1
src/mol-gl/shader/cylinders.vert.ts

@@ -8,6 +8,8 @@ export const cylinders_vert = `
 precision highp float;
 precision highp int;
 
+#define multiColor
+
 #include common
 #include read_from_texture
 #include common_vert_params
@@ -26,12 +28,14 @@ attribute vec3 aStart;
 attribute vec3 aEnd;
 attribute float aScale;
 attribute float aCap;
+attribute float aColorMode;
 
 varying mat4 vTransform;
 varying vec3 vStart;
 varying vec3 vEnd;
 varying float vSize;
 varying float vCap;
+varying float vColorMode;
 
 uniform float uIsOrtho;
 uniform vec3 uCameraDir;
@@ -50,6 +54,7 @@ void main() {
     vEnd = (modelTransform * vec4(aEnd, 1.0)).xyz;
     vSize = size * aScale;
     vCap = aCap;
+    vColorMode = aColorMode;
 
     vModelPosition = (vStart + vEnd) * 0.5;
     vec3 camDir = -mix(normalize(vModelPosition - uCameraPosition), uCameraDir, uIsOrtho);
@@ -78,4 +83,4 @@ void main() {
 
     #include clip_instance
 }
-`;
+`;

+ 2 - 1
src/mol-repr/structure/visual/bond-inter-unit-cylinder.ts

@@ -235,7 +235,8 @@ 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.multipleBonds !== currentProps.multipleBonds ||
+                newProps.colorMode !== currentProps.colorMode
             );
 
             if (newStructure.interUnitBonds !== currentStructure.interUnitBonds) {

+ 2 - 1
src/mol-repr/structure/visual/bond-intra-unit-cylinder.ts

@@ -253,7 +253,8 @@ export function IntraUnitBondCylinderImpostorVisual(materialId: number): UnitsVi
                 !arrayEqual(newProps.excludeTypes, currentProps.excludeTypes) ||
                 newProps.adjustCylinderLength !== currentProps.adjustCylinderLength ||
                 newProps.aromaticBonds !== currentProps.aromaticBonds ||
-                newProps.multipleBonds !== currentProps.multipleBonds
+                newProps.multipleBonds !== currentProps.multipleBonds ||
+                newProps.colorMode !== currentProps.colorMode
             );
 
             const newUnit = newStructureGroup.group.units[0];

+ 3 - 3
src/mol-repr/structure/visual/polymer-backbone-cylinder.ts

@@ -70,8 +70,8 @@ function createPolymerBackboneCylinderImpostor(ctx: VisualContext, unit: Unit, s
         const shift = isNucleicType ? NucleicShift : StandardShift;
 
         v3add(pM, pA, v3scale(pM, v3sub(pM, pB, pA), shift));
-        builder.add(pA[0], pA[1], pA[2], pM[0], pM[1], pM[2], 1, false, false, groupA);
-        builder.add(pM[0], pM[1], pM[2], pB[0], pB[1], pB[2], 1, false, false, groupB);
+        builder.add(pA[0], pA[1], pA[2], pM[0], pM[1], pM[2], 1, false, false, 2, groupA);
+        builder.add(pM[0], pM[1], pM[2], pB[0], pB[1], pB[2], 1, false, false, 2, groupB);
     };
 
     eachPolymerBackboneLink(unit, add);
@@ -161,4 +161,4 @@ export function PolymerBackboneCylinderMeshVisual(materialId: number): UnitsVisu
             return props.tryUseImpostor && !!webgl;
         }
     }, materialId);
-}
+}

+ 18 - 15
src/mol-repr/structure/visual/util/link.ts

@@ -31,6 +31,7 @@ export const LinkCylinderParams = {
     dashCap: PD.Boolean(true),
     stubCap: PD.Boolean(true),
     radialSegments: PD.Numeric(16, { min: 2, max: 56, step: 2 }, BaseGeometry.CustomQualityParamInfo),
+    colorMode: PD.Select('default', PD.arrayToOptions(['default', 'interpolate']), BaseGeometry.ShadingCategory)
 };
 export const DefaultLinkCylinderProps = PD.getDefaultValues(LinkCylinderParams);
 export type LinkCylinderProps = typeof DefaultLinkCylinderProps
@@ -271,7 +272,9 @@ export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: Lin
 
     if (!linkCount) return { cylinders: Cylinders.createEmpty(cylinders) };
 
-    const { linkScale, linkSpacing, linkCap, aromaticScale, aromaticSpacing, aromaticDashCount, dashCount, dashScale, dashCap, stubCap } = props;
+    const { linkScale, linkSpacing, linkCap, aromaticScale, aromaticSpacing, aromaticDashCount, dashCount, dashScale, dashCap, stubCap, colorMode } = props;
+    const interpolate = colorMode === 'interpolate';
+    const colorModeFlag = interpolate === true ? 3 : 2;
 
     const cylindersCountEstimate = linkCount * 2;
     const builder = CylindersBuilder.create(cylindersCountEstimate, cylindersCountEstimate / 4, cylinders);
@@ -306,15 +309,15 @@ export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: Lin
 
         if (linkStyle === LinkStyle.Solid) {
             v3scale(vm, v3add(vm, va, vb), 0.5);
-            builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], 1, linkCap, linkStub, edgeIndex);
+            builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], 1, linkCap, linkStub, colorModeFlag, edgeIndex);
         } else if (linkStyle === LinkStyle.Dashed) {
             if (segmentCount > 1) {
                 v3scale(tmpV12, v3sub(tmpV12, vb, va), lengthScale);
                 v3sub(vb, vb, tmpV12);
-                builder.addFixedCountDashes(va, vb, segmentCount, dashScale, dashCap, dashCap, edgeIndex);
+                builder.addFixedCountDashes(va, vb, segmentCount, dashScale, dashCap, dashCap, interpolate, edgeIndex);
             } else {
                 v3scale(vm, v3add(vm, va, vb), 0.5);
-                builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], dashScale, dashCap, dashCap, edgeIndex);
+                builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], dashScale, dashCap, dashCap, colorModeFlag, edgeIndex);
             }
         } else if (linkStyle === LinkStyle.Double || linkStyle === LinkStyle.OffsetDouble || linkStyle === LinkStyle.Triple || linkStyle === LinkStyle.OffsetTriple || linkStyle === LinkStyle.Aromatic || linkStyle === LinkStyle.MirroredAromatic) {
             const order = (linkStyle === LinkStyle.Double || linkStyle === LinkStyle.OffsetDouble) ? 2 :
@@ -326,7 +329,7 @@ export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: Lin
             calculateShiftDir(vShift, va, vb, referencePosition ? referencePosition(edgeIndex) : null);
 
             if (linkStyle === LinkStyle.Aromatic || linkStyle === LinkStyle.MirroredAromatic) {
-                builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], 1, linkCap, linkStub, edgeIndex);
+                builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], 1, linkCap, linkStub, colorModeFlag, edgeIndex);
 
                 const aromaticOffset = linkRadius + aromaticScale * linkRadius + aromaticScale * linkRadius * aromaticSpacing;
 
@@ -339,38 +342,38 @@ export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: Lin
                 v3setMagnitude(vShift, vShift, aromaticOffset);
                 v3sub(va, va, vShift);
                 v3sub(vb, vb, vShift);
-                builder.addFixedCountDashes(va, vb, aromaticSegmentCount, aromaticScale, dashCap, dashCap, edgeIndex);
+                builder.addFixedCountDashes(va, vb, aromaticSegmentCount, aromaticScale, dashCap, dashCap, interpolate, edgeIndex);
 
                 if (linkStyle === LinkStyle.MirroredAromatic) {
                     v3setMagnitude(vShift, vShift, aromaticOffset * 2);
                     v3add(va, va, vShift);
                     v3add(vb, vb, vShift);
-                    builder.addFixedCountDashes(va, vb, aromaticSegmentCount, aromaticScale, dashCap, dashCap, edgeIndex);
+                    builder.addFixedCountDashes(va, vb, aromaticSegmentCount, aromaticScale, dashCap, dashCap, interpolate, edgeIndex);
                 }
             } else if (linkStyle === LinkStyle.OffsetDouble || linkStyle === LinkStyle.OffsetTriple) {
                 const multipleOffset = linkRadius + multiScale * linkRadius + linkScale * linkRadius * linkSpacing;
                 v3setMagnitude(vShift, vShift, multipleOffset);
 
-                builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], 1, linkCap, linkStub, edgeIndex);
+                builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], 1, linkCap, linkStub, colorModeFlag, edgeIndex);
 
                 v3setMagnitude(tmpV12, v3sub(tmpV12, va, vb), linkRadius / 1.5);
                 v3sub(va, va, tmpV12);
 
-                if (order === 3) builder.add(va[0] + vShift[0], va[1] + vShift[1], va[2] + vShift[2], vm[0] + vShift[0], vm[1] + vShift[1], vm[2] + vShift[2], multiScale, linkCap, linkStub, edgeIndex);
-                builder.add(va[0] - vShift[0], va[1] - vShift[1], va[2] - vShift[2], vm[0] - vShift[0], vm[1] - vShift[1], vm[2] - vShift[2], multiScale, dashCap, linkStub, edgeIndex);
+                if (order === 3) builder.add(va[0] + vShift[0], va[1] + vShift[1], va[2] + vShift[2], vm[0] + vShift[0], vm[1] + vShift[1], vm[2] + vShift[2], multiScale, linkCap, linkStub, colorModeFlag, edgeIndex);
+                builder.add(va[0] - vShift[0], va[1] - vShift[1], va[2] - vShift[2], vm[0] - vShift[0], vm[1] - vShift[1], vm[2] - vShift[2], multiScale, dashCap, linkStub, colorModeFlag, edgeIndex);
             } else {
                 v3setMagnitude(vShift, vShift, absOffset);
 
-                if (order === 3) builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], multiScale, linkCap, linkStub, edgeIndex);
-                builder.add(va[0] + vShift[0], va[1] + vShift[1], va[2] + vShift[2], vm[0] + vShift[0], vm[1] + vShift[1], vm[2] + vShift[2], multiScale, linkCap, linkStub, edgeIndex);
-                builder.add(va[0] - vShift[0], va[1] - vShift[1], va[2] - vShift[2], vm[0] - vShift[0], vm[1] - vShift[1], vm[2] - vShift[2], multiScale, linkCap, linkStub, edgeIndex);
+                if (order === 3) builder.add(va[0], va[1], va[2], vm[0], vm[1], vm[2], multiScale, linkCap, linkStub, colorModeFlag, edgeIndex);
+                builder.add(va[0] + vShift[0], va[1] + vShift[1], va[2] + vShift[2], vm[0] + vShift[0], vm[1] + vShift[1], vm[2] + vShift[2], multiScale, linkCap, linkStub, colorModeFlag, edgeIndex);
+                builder.add(va[0] - vShift[0], va[1] - vShift[1], va[2] - vShift[2], vm[0] - vShift[0], vm[1] - vShift[1], vm[2] - vShift[2], multiScale, linkCap, linkStub, colorModeFlag, edgeIndex);
             }
         } else if (linkStyle === LinkStyle.Disk) {
             v3scale(tmpV12, v3sub(tmpV12, vb, va), 0.475);
             v3add(va, va, tmpV12);
             v3sub(vb, vb, tmpV12);
 
-            builder.add(va[0], va[1], va[2], vb[0], vb[1], vb[2], 1, linkCap, linkStub, edgeIndex);
+            builder.add(va[0], va[1], va[2], vb[0], vb[1], vb[2], 1, linkCap, linkStub, colorModeFlag, edgeIndex);
         }
     }
 
@@ -505,4 +508,4 @@ export function createLinkLines(ctx: VisualContext, linkBuilder: LinkBuilderProp
     } else {
         return { lines: l };
     }
-}
+}