|
@@ -6,7 +6,7 @@
|
|
|
*/
|
|
|
|
|
|
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
|
|
-import { Vec3 } from '../../mol-math/linear-algebra';
|
|
|
+import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
|
|
|
import { Representation, RepresentationContext, RepresentationParamsGetter } from '../../mol-repr/representation';
|
|
|
import { Structure } from '../../mol-model/structure';
|
|
|
import { Spheres } from '../../mol-geo/geometry/spheres/spheres';
|
|
@@ -19,13 +19,55 @@ import { ShapeRepresentation } from '../../mol-repr/shape/representation';
|
|
|
import { Shape } from '../../mol-model/shape';
|
|
|
import { ColorNames } from '../../mol-util/color/names';
|
|
|
import { RuntimeContext } from '../../mol-task';
|
|
|
+import { Lines } from '../../mol-geo/geometry/lines/lines';
|
|
|
+import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
|
|
|
+import { LinesBuilder } from '../../mol-geo/geometry/lines/lines-builder';
|
|
|
+import { Circle } from '../../mol-geo/primitive/circle';
|
|
|
+import { transformPrimitive } from '../../mol-geo/primitive/primitive';
|
|
|
+import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
|
|
|
|
|
|
-const MembraneOrientationParams = {
|
|
|
+const SharedParams = {
|
|
|
+ color: PD.Color(ColorNames.lightgrey)
|
|
|
+};
|
|
|
+
|
|
|
+const BilayerSpheresParams = {
|
|
|
+ ...SharedParams,
|
|
|
...Spheres.Params,
|
|
|
- color: PD.Color(ColorNames.lightgrey),
|
|
|
size: PD.Numeric(1, { min: 0.1, max: 10, step: 0.1 }, { description: 'Size of spheres that represent membrane planes' }),
|
|
|
density: PD.Numeric(1, { min: 0.25, max: 10, step: 0.25 }, { description: 'Distance between spheres'})
|
|
|
};
|
|
|
+export type BilayerSpheresParams = typeof BilayerSpheresParams
|
|
|
+export type BilayerSpheresProps = PD.Values<BilayerSpheresParams>
|
|
|
+
|
|
|
+const BilayerPlanesParams = {
|
|
|
+ ...SharedParams,
|
|
|
+ ...Mesh.Params
|
|
|
+};
|
|
|
+export type BilayerPlanesParams = typeof BilayerPlanesParams
|
|
|
+export type BilayerPlanesProps = PD.Values<BilayerPlanesParams>
|
|
|
+
|
|
|
+const BilayerRimsParams = {
|
|
|
+ ...SharedParams,
|
|
|
+ ...Lines.Params,
|
|
|
+ lineSizeAttenuation: PD.Boolean(true),
|
|
|
+ linesSize: PD.Numeric(0.04, { min: 0.01, max: 5, step: 0.01 }),
|
|
|
+ dashLength: PD.Numeric(0.04, { min: 0.01, max: 0.2, step: 0.01 })
|
|
|
+};
|
|
|
+export type BilayerRimsParams = typeof BilayerRimsParams
|
|
|
+export type BilayerRimsProps = PD.Values<BilayerRimsParams>
|
|
|
+
|
|
|
+const MembraneOrientationVisuals = {
|
|
|
+ 'bilayer-spheres': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneOrientation, BilayerSpheresParams>) => ShapeRepresentation(getBilayerSpheres, Spheres.Utils, { modifyState: s => ({ ...s, pickable: false }) }),
|
|
|
+ 'bilayer-planes': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneOrientation, BilayerPlanesParams>) => ShapeRepresentation(getBilayerPlanes, Mesh.Utils, { modifyState: s => ({ ...s, pickable: false }) }),
|
|
|
+ 'bilayer-rims': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneOrientation, BilayerRimsParams>) => ShapeRepresentation(getBilayerRims, Lines.Utils, { modifyState: s => ({ ...s, pickable: false }) })
|
|
|
+};
|
|
|
+
|
|
|
+export const MembraneOrientationParams = {
|
|
|
+ ...BilayerSpheresParams,
|
|
|
+ ...BilayerPlanesParams,
|
|
|
+ ...BilayerRimsParams,
|
|
|
+ visuals: PD.MultiSelect(['bilayer-spheres', 'bilayer-planes', 'bilayer-rims'], PD.objectToOptions(MembraneOrientationVisuals)),
|
|
|
+};
|
|
|
export type MembraneOrientationParams = typeof MembraneOrientationParams
|
|
|
export type MembraneOrientationProps = PD.Values<MembraneOrientationParams>
|
|
|
|
|
@@ -38,11 +80,6 @@ export function MembraneOrientationRepresentation(ctx: RepresentationContext, ge
|
|
|
return Representation.createMulti('Membrane Orientation', ctx, getParams, StructureRepresentationStateBuilder, MembraneOrientationVisuals as unknown as Representation.Def<Structure, MembraneOrientationParams>);
|
|
|
}
|
|
|
|
|
|
-const MembraneOrientationVisuals = {
|
|
|
- 'membrane-orientation': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneOrientation, MembraneOrientationParams>) => ShapeRepresentation(getMembraneSpheres, Spheres.Utils),
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
export const MembraneOrientationRepresentationProvider = StructureRepresentationProvider({
|
|
|
name: 'membrane-orientation',
|
|
|
label: 'Membrane Orientation',
|
|
@@ -55,24 +92,74 @@ export const MembraneOrientationRepresentationProvider = StructureRepresentation
|
|
|
isApplicable: (structure: Structure) => structure.elementCount > 0
|
|
|
});
|
|
|
|
|
|
-function getMembraneSpheres(ctx: RuntimeContext, data: Structure, props: MembraneOrientationProps) {
|
|
|
+function getBilayerRims(ctx: RuntimeContext, data: Structure, props: BilayerRimsProps): Shape<Lines> {
|
|
|
+ const { p1, p2, centroid, normal, radius } = MembraneOrientationProvider.get(data).value!;
|
|
|
+ const builder = LinesBuilder.create(128, 64);
|
|
|
+ getLayerCircle(builder, p1, centroid, normal, radius);
|
|
|
+ getLayerCircle(builder, p2, centroid, normal, radius);
|
|
|
+ return Shape.create(name, data, builder.getLines(), () => props.color, () => /*props.linesSize*/1, () => '');
|
|
|
+}
|
|
|
+
|
|
|
+function getLayerCircle(builder: LinesBuilder, p: Vec3, centroid: Vec3, normal: Vec3, radius: number) {
|
|
|
+ const circle = getCircle(p, centroid, normal, radius);
|
|
|
+ const { indices, vertices } = circle;
|
|
|
+ for (let j = 0, jl = indices.length; j < jl; j += 3) {
|
|
|
+ if (j % 2 === 1) continue; // draw every other segment to get dashes
|
|
|
+ const start = indices[j] * 3;
|
|
|
+ const end = indices[j + 1] * 3;
|
|
|
+ const startX = vertices[start];
|
|
|
+ const startY = vertices[start + 1];
|
|
|
+ const startZ = vertices[start + 2];
|
|
|
+ const endX = vertices[end];
|
|
|
+ const endY = vertices[end + 1];
|
|
|
+ const endZ = vertices[end + 2];
|
|
|
+ builder.add(startX, startY, startZ, endX, endY, endZ, 0);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const tmpMat = Mat4();
|
|
|
+function getCircle(p: Vec3, centroid: Vec3, normal: Vec3, radius: number) {
|
|
|
+ Mat4.targetTo(tmpMat, p, centroid, normal);
|
|
|
+ Mat4.setTranslation(tmpMat, p);
|
|
|
+ Mat4.mul(tmpMat, tmpMat, Mat4.rotX90);
|
|
|
+
|
|
|
+ const circle = Circle({ radius });
|
|
|
+ return transformPrimitive(circle, tmpMat);
|
|
|
+}
|
|
|
+
|
|
|
+function getBilayerPlanes(ctx: RuntimeContext, data: Structure, props: BilayerPlanesProps): Shape<Mesh> {
|
|
|
+ const { p1, p2, centroid, normal, radius } = MembraneOrientationProvider.get(data).value!;
|
|
|
+ const state = MeshBuilder.createState(128, 64);
|
|
|
+ getLayerPlane(state, p1, centroid, normal, radius);
|
|
|
+ getLayerPlane(state, p2, centroid, normal, radius);
|
|
|
+ return Shape.create(name, data, MeshBuilder.getMesh(state), () => props.color, () => /*props.size*/1, () => '');
|
|
|
+}
|
|
|
+
|
|
|
+function getLayerPlane(state: MeshBuilder.State, p: Vec3, centroid: Vec3, normal: Vec3, radius: number) {
|
|
|
+ const circle = getCircle(p, centroid, normal, radius);
|
|
|
+ state.currentGroup = 0;
|
|
|
+ MeshBuilder.addPrimitive(state, Mat4.id, circle);
|
|
|
+ MeshBuilder.addPrimitiveFlipped(state, Mat4.id, circle);
|
|
|
+}
|
|
|
+
|
|
|
+function getBilayerSpheres(ctx: RuntimeContext, data: Structure, props: BilayerSpheresProps): Shape<Spheres> {
|
|
|
const { density } = props;
|
|
|
- const { radius, p1, p2, normal } = MembraneOrientationProvider.get(data).value!;;
|
|
|
+ const { radius, p1, p2, normal } = MembraneOrientationProvider.get(data).value!;
|
|
|
|
|
|
const spheresBuilder = SpheresBuilder.create();
|
|
|
- createMembraneLayer(spheresBuilder, p1, normal, density, radius);
|
|
|
- createMembraneLayer(spheresBuilder, p2, normal, density, radius);
|
|
|
- return Shape.create(name, data, spheresBuilder.getSpheres(), () => props.color, () => props.size, () => 'Membrane Boundaries');
|
|
|
+ getLayerSpheres(spheresBuilder, p1, normal, density, radius * radius);
|
|
|
+ getLayerSpheres(spheresBuilder, p2, normal, density, radius * radius);
|
|
|
+ return Shape.create(name, data, spheresBuilder.getSpheres(), () => props.color, () => props.size, () => '');
|
|
|
}
|
|
|
|
|
|
-function createMembraneLayer(spheresBuilder: SpheresBuilder, point: Vec3, normalVector: Vec3, density: number, radius: number) {
|
|
|
+function getLayerSpheres(spheresBuilder: SpheresBuilder, point: Vec3, normalVector: Vec3, density: number, sqRadius: number) {
|
|
|
Vec3.normalize(normalVector, normalVector);
|
|
|
const d = -Vec3.dot(normalVector, point);
|
|
|
const rep = Vec3();
|
|
|
for (let i = -1000, il = 1000; i < il; i += density) {
|
|
|
for (let j = -1000, jl = 1000; j < jl; j += density) {
|
|
|
Vec3.set(rep, i, j, normalVector[2] === 0 ? 0 : -(d + i * normalVector[0] + j * normalVector[1]) / normalVector[2]);
|
|
|
- if (Vec3.squaredDistance(rep, point) < radius) {
|
|
|
+ if (Vec3.squaredDistance(rep, point) < sqRadius) {
|
|
|
spheresBuilder.add(rep[0], rep[1], rep[2], 0);
|
|
|
}
|
|
|
}
|