Skip to content

Commit

Permalink
Curve/Protein/RibbonDiagram: Clean up and documentation improved
Browse files Browse the repository at this point in the history
  • Loading branch information
skalarproduktraum committed Jul 10, 2024
1 parent fa6d165 commit 177673a
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 59 deletions.
43 changes: 42 additions & 1 deletion src/main/kotlin/graphics/scenery/geometry/curve/Curve.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,48 @@ import org.joml.Vector2f
import org.joml.Vector3f

data class Vertex(val v: Vector3f, val n: Vector3f, val uv: Vector2f)
data class Shape(val vertices: List<Vertex>)
data class Shape(val vertices: List<Vertex>) {
companion object {
/**
* Returns the vertices for a rectangle.
*/
fun makeRectangle() = Shape(listOf(
Vertex(Vector3f(0.9f, 0f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(0f, 0.1f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(-0.9f, 0f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(0f, -0.1f, 0f), Vector3f(), Vector2f())
))

private val sin45 = kotlin.math.sqrt(2f) / 40f

/**
* Returns the vertices for an octogon.
*/
fun makeOctagon() = Shape(listOf(
Vertex(Vector3f(0.05f, 0f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(sin45, sin45, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(0f, 0.05f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(-sin45, sin45, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(-0.05f, 0f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(-sin45, -sin45, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(0f, -0.05f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(sin45, -sin45, 0f), Vector3f(), Vector2f())
))

/**
* Returns vertices for a reversed rectangle.
*/
fun makeReversedRectangle() = Shape(listOf(
Vertex(Vector3f(0.1f, 0.8f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(-0.1f, 0.8f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(-0.1f, -0.8f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(0.1f, -0.8f, 0f), Vector3f(), Vector2f())
))


}
}

typealias SegmentedShapeList = List<Shape>

fun List<Vector3f>.toShape(): Shape = Shape(this.map { Vertex(it, it, Vector2f()) })

Check warning on line 52 in src/main/kotlin/graphics/scenery/geometry/curve/Curve.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/kotlin/graphics/scenery/geometry/curve/Curve.kt#L52

The function toShape is missing documentation.
Expand Down
24 changes: 11 additions & 13 deletions src/main/kotlin/graphics/scenery/geometry/curve/DefaultCurve.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import graphics.scenery.geometry.curve.FrenetCurve.Companion.computeFrenetFrames
import graphics.scenery.utils.extensions.minus
import graphics.scenery.utils.extensions.plus
import graphics.scenery.utils.extensions.plusAssign
import graphics.scenery.utils.extensions.times
import org.joml.Vector2f
import org.joml.Vector3f
import java.nio.FloatBuffer
Expand Down Expand Up @@ -58,10 +57,18 @@ open class DefaultCurve(
//default behaviour: if the last section is a single shape,
// it will be added to the last window of the shapes
if (index == subShapes.lastIndex - 1 && subShapes.last().size == 1) {
// if there is only a single shape, we need to add a cover for both sides,
// so we override the default cover variable here
val cv = if(subShapes.size == 2) {
CurveCover.Both
} else {
cover
}

val arrayListShapes = listShapes as ArrayList
arrayListShapes.add(transformedBaseShapes.last())
val (vertices, normals) = calculateTriangles(arrayListShapes,
cover = CurveCover.Bottom)
cover = cv)
val partialCurve = PartialCurve(vertices, normals)
this.addChild(partialCurve)
} else {
Expand All @@ -77,18 +84,13 @@ open class DefaultCurve(
v1: graphics.scenery.geometry.curve.Vertex,
v2: graphics.scenery.geometry.curve.Vertex,
v3: graphics.scenery.geometry.curve.Vertex,
vertices: FloatBuffer,
normals: MutableList<Vector3f>?
vertices: FloatBuffer
): Vector3f {
vertices += v1.v
vertices += v3.v
vertices += v2.v

val normal = ((v3.v - v1.v).cross(v2.v - v1.v)).normalize()
normals?.add(normal)
normals?.add(normal)
normals?.add(normal)
return normal
return ((v3.v - v1.v).cross(v2.v - v1.v)).normalize()
}

/**
Expand Down Expand Up @@ -130,15 +132,13 @@ open class DefaultCurve(
v2 = currentShape.vertices[vertexIndex + 1],
v3 = nextShape.vertices[vertexIndex],
vertices = verticesWithoutCoverBuffer,
normals = null
)

val n2 = addVN(
v1 = currentShape.vertices[vertexIndex + 1],
v2 = nextShape.vertices[vertexIndex + 1],
v3 = nextShape.vertices[vertexIndex],
vertices = verticesWithoutCoverBuffer,
normals = null
)

val faceNormal = (n1 + n2).normalize()
Expand All @@ -155,15 +155,13 @@ open class DefaultCurve(
v2 = currentShape.vertices[0],
v3 = nextShape.vertices[shape.vertices.lastIndex],
vertices = verticesWithoutCoverBuffer,
normals = null
)

val n2 = addVN(
v1 = currentShape.vertices[0],
v2 = nextShape.vertices[0],
v3 = nextShape.vertices[shape.vertices.lastIndex],
vertices = verticesWithoutCoverBuffer,
normals = null
)
val faceNormal = (n1 + n2).normalize()
intermediateNormalSection.add(faceNormal)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package graphics.scenery.proteins

import graphics.scenery.*
import graphics.scenery.primitives.Cylinder
import graphics.scenery.proteins.RibbonDiagram.GuidePointCalculation.getVector
import graphics.scenery.proteins.RibbonDiagram.Companion.getVector
import org.biojava.nbio.structure.Bond
import org.biojava.nbio.structure.BondImpl
import org.biojava.nbio.structure.Element
Expand Down
16 changes: 16 additions & 0 deletions src/main/kotlin/graphics/scenery/proteins/Protein.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import org.biojava.nbio.structure.io.PDBFileReader
import java.io.FileNotFoundException
import java.io.IOException
import java.nio.file.InvalidPathException
import java.nio.file.Paths
import kotlin.io.path.createDirectories

/**
* Constructs a protein from a pdb-file.
Expand All @@ -24,6 +26,20 @@ class Protein(val structure: Structure): Mesh("Protein") {
companion object MyProtein {
private val proteinLogger by lazyLogger()

init {
val pdbDirectoryProperty = System.getProperty("scenery.Proteins.PDBDirectory", System.getProperty("user.home") + "/.scenery/pdb")
val pdbCacheDirectoryProperty = System.getProperty("scenery.Proteins.PDBCacheDirectory", System.getProperty("user.home") + "/.scenery/pdb-cache")

val pdbDir = Paths.get(pdbDirectoryProperty)
pdbDir.createDirectories()

val pdbCacheDir = Paths.get(pdbCacheDirectoryProperty)
pdbCacheDir.createDirectories()

System.setProperty("PDB_DIR", pdbDir.toAbsolutePath().toString())
System.setProperty("PDB_CACHE_DIR", pdbCacheDir.toAbsolutePath().toString())
}

fun fromID(id: String): Protein {
try {
StructureIO.getStructure(id)
Expand Down
69 changes: 28 additions & 41 deletions src/main/kotlin/graphics/scenery/proteins/RibbonDiagram.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,19 @@ import kotlin.math.min
* by Richardson et al.)
*
* @author Justin Buerger <[email protected]>
* @param [protein] the polypeptide you wish to visualize, stored in the class Protein
* @param [protein] The polypeptide you wish to visualize, stored in the class Protein
*
* @param [showSecondaryStructures] Whether or not to show secondary structures as well
*
* @param [verticesPerSection] How many vertices should be rendered per section. The default is 20, and can also
* be overridden by the system property `scenery.Proteins.VerticesPerSection`.
*/

class RibbonDiagram(val protein: Protein, private val showSecondaryStructures: Boolean = false): Mesh("ribbon") {
class RibbonDiagram(
val protein: Protein,
private val showSecondaryStructures: Boolean = false,
val verticesPerSection: Int = System.getProperty("scenery.Proteins.VerticesPerSection", "20").toInt()
): Mesh("ribbon") {

/*
*[structure] the structure of the protein stored in the class Structure of BioJava
Expand All @@ -75,12 +84,11 @@ class RibbonDiagram(val protein: Protein, private val showSecondaryStructures: B
*/
private val structure = protein.structure
private val chains = structure.chains
val groups = chains.flatMap { it.atomGroups }
private val groups = chains.flatMap { it.atomGroups }
private val widthAlpha = 2.0f
private val widthBeta = 2.2f
private val widthCoil = 1.0f
private val chainList = ArrayList<List<Group>>(groups.size)
private val sectionVerticesCount = 20
private val secStruc = dssp()
//calculate the centroid of the protein
private val centroid = Axis.getCentroid(groups.flatMap { it.atoms }.filter{it.name == "CA"}.map{it.getVector()})
Expand Down Expand Up @@ -129,31 +137,6 @@ class RibbonDiagram(val protein: Protein, private val showSecondaryStructures: B
{ splinePointCentered.subList(0, splinePointCentered.lastIndex) }
else{splinePointCentered}

val rectangle = Shape(listOf(
Vertex(Vector3f(0.9f, 0f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(0f, 0.1f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(-0.9f, 0f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(0f, -0.1f, 0f), Vector3f(), Vector2f())
))

val sin45 = kotlin.math.sqrt(2f) / 40f
val octagon = Shape(listOf(
Vertex(Vector3f(0.05f, 0f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(sin45, sin45, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(0f, 0.05f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(-sin45, sin45, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(-0.05f, 0f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(-sin45, -sin45, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(0f, -0.05f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(sin45, -sin45, 0f), Vector3f(), Vector2f())
))

val reversedRectangle = Shape(listOf(
Vertex(Vector3f(0.1f, 0.8f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(-0.1f, 0.8f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(-0.1f, -0.8f, 0f), Vector3f(), Vector2f()),
Vertex(Vector3f(0.1f, -0.8f, 0f), Vector3f(), Vector2f())
))
/*
In the following lines of code(144-211), we build a curve for each secondary structure. How does this work, step
by step? First, we iterate through the guide points. A guide point represents a residue, therefore, it can be
Expand Down Expand Up @@ -181,7 +164,7 @@ class RibbonDiagram(val protein: Protein, private val showSecondaryStructures: B
val coils = Mesh("coil")

//exclude dummy points for spline calculation
val ssSections = getSecondaryStructureLengths(guidePointList.drop(1).dropLast(1), splinePoints, sectionVerticesCount)
val ssSections = getSecondaryStructureLengths(guidePointList.drop(1).dropLast(1), splinePoints, verticesPerSection)
//configure offset for helix computation below
var guidePointOffset = 1
//iterate through all secondary structure sections
Expand All @@ -199,15 +182,15 @@ class RibbonDiagram(val protein: Protein, private val showSecondaryStructures: B
}
val axis = Axis(caList)
val axisLine = PositionDirection(axis.position, axis.direction)
val helixCurve = Helix(axisLine, DummySpline(subSpline, sectionVerticesCount)) { listOf(rectangle) }
val helixCurve = Helix(axisLine, DummySpline(subSpline, verticesPerSection)) { listOf(Shape.makeRectangle()) }
if (showSecondaryStructures) {
alphas.addChild(helixCurve)
} else {
subParent.addChild(helixCurve)
}
}
else {
val ssSubList = ArrayList<Shape>(sectionVerticesCount * length)
val ssSubList = ArrayList<Shape>(verticesPerSection * length)

val iterationLength = subSpline.size

Expand All @@ -216,7 +199,7 @@ class RibbonDiagram(val protein: Protein, private val showSecondaryStructures: B
if (type.isBetaStrand) {
val seventyPercent = (iterationLength * 0.70).toInt()
for (j in 0 until seventyPercent) {
ssSubList.add(reversedRectangle)
ssSubList.add(Shape.makeReversedRectangle())
}
val thirtyPercent = iterationLength - seventyPercent
for (j in thirtyPercent downTo 1) {
Expand All @@ -232,18 +215,18 @@ class RibbonDiagram(val protein: Protein, private val showSecondaryStructures: B
)
}
val betaCurve = DefaultCurve(
DummySpline(subSpline, sectionVerticesCount), { ssSubList })
DummySpline(subSpline, verticesPerSection), { ssSubList })
if (showSecondaryStructures) {
betas.addChild(betaCurve)
} else {
subParent.addChild(betaCurve)
}
} else {
for (j in 0 until iterationLength) {
ssSubList.add(octagon)
ssSubList.add(Shape.makeOctagon())
}
val coilCurve =
DefaultCurve(DummySpline(subSpline, sectionVerticesCount), { ssSubList })
DefaultCurve(DummySpline(subSpline, verticesPerSection), { ssSubList })
if (showSecondaryStructures) {
coils.addChild(coilCurve)
} else {
Expand Down Expand Up @@ -303,24 +286,27 @@ class RibbonDiagram(val protein: Protein, private val showSecondaryStructures: B
* (a spline which list of controlPoints is equal to its guidePoints)
*/
private fun ribbonSpline(guidePoints: ArrayList<GuidePoint>): Spline {
val finalSpline = ArrayList<Vector3f>(guidePoints.size * sectionVerticesCount)
val finalSpline = ArrayList<Vector3f>(guidePoints.size * verticesPerSection)
val skeleton = splineSkeleton(guidePoints)
val spline1 = UniformBSpline(skeleton.splineSkeleton1, sectionVerticesCount).splinePoints()
val spline2 = UniformBSpline(skeleton.splineSkeleton2, sectionVerticesCount).splinePoints()
val spline1 = UniformBSpline(skeleton.splineSkeleton1, verticesPerSection).splinePoints()
val spline2 = UniformBSpline(skeleton.splineSkeleton2, verticesPerSection).splinePoints()
spline1.forEachIndexed { i, _ ->
val splinePoint = Vector3f()
spline1[i].add(spline2[i], splinePoint)
splinePoint.mul(0.5f)
finalSpline.add(splinePoint)
}
return DummySpline(finalSpline, sectionVerticesCount)
return DummySpline(finalSpline, verticesPerSection)
}

companion object GuidePointCalculation {
companion object {



/**
* Calculates the GuidePoints from the list of amino acids.
*/
@JvmStatic
fun calculateGuidePoints(aminoList: List<Group>, ssList: List<SecStrucElement>): ArrayList<GuidePoint> {
//To include all points in the visualization, dummy points need to be made.
//First, a list without these dummy points is calculated.
Expand Down Expand Up @@ -510,6 +496,7 @@ class RibbonDiagram(val protein: Protein, private val showSecondaryStructures: B
* Extension function to make a Vector out of an atom position. We do not
* need any information about an atom besides its name and its position.
*/
@JvmStatic
fun Atom.getVector(): Vector3f {
return Vector3f(this.x.toFloat(), this.y.toFloat(), this.z.toFloat())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ class VRSideChainsExample : SceneryBase(

scene.addChild(cam)

//protein = Protein.fromFile("C:\\Users\\Justin\\Downloads\\7ch1_SLC26A9.pdb")
protein = Protein.fromID("3nir")
sideChains = AminoAcidsStickAndBall(protein)
scene.addChild(sideChains)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ class RibbonDiagramTests {
if (dsspPlant is List<*>) {
@Suppress("UNCHECKED_CAST")
val guides =
RibbonDiagram.GuidePointCalculation.calculateGuidePoints(it, dsspPlant as List<SecStrucElement>)
RibbonDiagram.calculateGuidePoints(it, dsspPlant as List<SecStrucElement>)
val spline = plantRibbon.callPrivateFunc("ribbonSpline", guides) as DummySpline
allPlantPoints += spline.splinePoints().size
}
}
//the protein has 46 residues, each section has ten spline points, and the whole spline one starting point
assertEquals(allPlantPoints, (46) * (10) +1)
assertEquals(allPlantPoints, plantProtein.getResidues().flatten().size * plantRibbon.verticesPerSection + 1)
}

/**
Expand Down

0 comments on commit 177673a

Please sign in to comment.