<commit_before>### define
model : Model
model/route : Route
libs/event_mixin : EventMixin
###

PLANE_XY = 0
PLANE_YZ = 1
PLANE_XZ = 2
VIEW_3D  = 3

TYPE_BRANCH = 1

COLOR_NORMAL = 0xff0000
COLOR_ACTIVE = 0x0000ff
COLOR_BRANCH = 0x550000
COLOR_BRANCH_ACTIVE = 0x000055

class Skeleton

  # This class is supposed to collect all the Geometries that belong to the skeleton, like
  # nodes, edges and trees

  constructor : (maxRouteLen, flycam, model) ->
    _.extend(this, new EventMixin())

    @maxRouteLen  = maxRouteLen
    @flycam       = flycam
    @model        = model
    # Edges
    @routes       = []
    # Nodes
    @nodes        = []
    @nodesSpheres = []
    # Tree IDs
    @ids          = []
    # Current Index
    @curIndex     = []

    # whether or not display the spheres
    @disSpheres   = true

    # Create sphere to represent the active Node, radius is
    # 1 nm, so that @activeNode.scale is the radius in nm.
    @activeNode = new THREE.Mesh(
        new THREE.SphereGeometry(1 / @model.Route.scaleX),
        new THREE.MeshLambertMaterial({
          color : COLOR_ACTIVE
          #transparent: true
          #opacity: 0.5 })
          })
      )
    @activeNode.doubleSided = true

    @reset()

  createNewTree : (treeId) ->
    # create route to show in previewBox and pre-allocate buffers
    routeGeometry = new THREE.Geometry()
    routeGeometryNodes = new THREE.Geometry()
    routeGeometry.dynamic = true
    routeGeometryNodes.dynamic = true

    for i in [1..@maxRouteLen]
      # workaround to hide the unused vertices
      routeGeometry.vertices.push(new THREE.Vector2(0,0))      # sources
      routeGeometry.vertices.push(new THREE.Vector2(0,0))      # targets
      routeGeometryNodes.vertices.push(new THREE.Vector2(0,0)) # nodes

    @routes.push(new THREE.Line(routeGeometry, new THREE.LineBasicMaterial({color: COLOR_NORMAL, linewidth: 1}), THREE.LinePieces))
    @nodes.push(new THREE.ParticleSystem(routeGeometryNodes, new THREE.ParticleBasicMaterial({color: COLOR_NORMAL, size: 5, sizeAttenuation : false})))
    @ids.push(treeId)
    @curIndex.push(0)

    # Initialize the tree
    @clearRoute(treeId)

    @setActiveNode()
    
    @trigger "newGeometries", [@routes[@routes.length - 1], @nodes[@nodes.length - 1]]

  clearRoute : (treeId) ->
    index = @getIndexFromTreeId(treeId)
    for i in [0..@maxRouteLen - 1]
      # workaround to hide the unused vertices
      @routes[index].geometry.vertices[2 * i]     = new THREE.Vector2(0,0)
      @routes[index].geometry.vertices[2 * i + 1] = new THREE.Vector2(0,0)
      @nodes[index].geometry.vertices[i] = new THREE.Vector2(0,0)
      @routes[index].geometry.verticesNeedUpdate = true
      @nodes[index].geometry.verticesNeedUpdate = true
    @curIndex[index] = 0

  reset : ->
    if (@ids.length > 0)
      @trigger "removeGeometries", @routes.concat(@nodes).concat(@nodesSpheres)
    @routes       = []
    @nodes        = []
    @ids          = []
    @nodesSpheres = []
    for tree in @model.Route.getTrees()
      @createNewTree(tree.treeId)
    @loadSkeletonFromModel()
    # Add Spheres to the scene
    @trigger "newGeometries", @nodesSpheres

  loadSkeletonFromModel : ->
    for tree in @model.Route.getTrees()
      nodeList = @model.Route.getNodeList(tree)

      index = @getIndexFromTreeId(tree.treeId)

      # Check that we are not dealing with a sentinel
      if nodeList[0].id
        # Draw first Node, because it has no parent and therefore isn't drawn in the loop below
        if nodeList.length > 0
          radius = nodeList[0].size
          nodePos = nodeList[0].pos
          @nodes[index].geometry.vertices[@curIndex[index]]    = new THREE.Vector3(nodePos[0], nodePos[1], nodePos[2])
          # Assign the ID to the vertex, so we can access it later
          @nodes[index].geometry.vertices[@curIndex[index]].nodeId = nodeList[0].id
          @pushNewNode(radius, nodePos, nodeList[0].id)
        @curIndex[index]++
        for node in nodeList
          if node.parent
            radius = node.size
            nodePos = node.parent.pos
            node2Pos = node.pos
            @routes[index].geometry.vertices[2 * @curIndex[index]]     = new THREE.Vector3(nodePos[0], nodePos[1], nodePos[2])
            @routes[index].geometry.vertices[2 * @curIndex[index] + 1] = new THREE.Vector3(node2Pos[0], node2Pos[1], node2Pos[2])
            @nodes[index].geometry.vertices[@curIndex[index]]    = new THREE.Vector3(node2Pos[0], node2Pos[1], node2Pos[2])
            # Assign the ID to the vertex, so we can access it later
            @nodes[index].geometry.vertices[@curIndex[index]].nodeId = node.id
            @pushNewNode(radius, node2Pos, node.id)
            @curIndex[index]++
        @routes[index].geometry.verticesNeedUpdate = true
        @nodes[index].geometry.verticesNeedUpdate = true
    @setActiveNode()

  setActiveNode : =>
    id = @model.Route.getActiveNodeId()
    position = @model.Route.getActiveNodePos()
    if @activeNodeSphere and @disSpheres==true
      @activeNodeSphere.visible = true
    # May be null
    @lastNodePosition = position
    if position
      @activeNode.visible = true
      @activeNodeSphere = @getSphereFromId(id)
      # Hide activeNodeSphere, because activeNode is visible anyway
      if @activeNodeSphere
        @activeNodeSphere.visible = false
        if @model.Route.getActiveNodeType() == TYPE_BRANCH
          @activeNode.material.color.setHex(COLOR_BRANCH_ACTIVE)
        else
          @activeNode.material.color.setHex(COLOR_ACTIVE)
      @setNodeRadius(@model.Route.getActiveNodeRadius())
      @activeNode.position = new THREE.Vector3(position[0], position[1], position[2])
    else
      @activeNodeSphere = null
      @activeNode.visible = false
    @flycam.hasChanged = true

  setBranchPoint : (isBranchPoint) ->
    colorActive = if isBranchPoint then COLOR_BRANCH_ACTIVE else COLOR_ACTIVE
    colorNormal = if isBranchPoint then COLOR_BRANCH else COLOR_NORMAL
    @activeNode.material.color.setHex(colorActive)
    if @activeNodeSphere
      @activeNodeSphere.material.color.setHex(colorNormal)
    @flycam.hasChanged = true

  setNodeRadius : (value) -><commit_msg>OX-129 set branch point on initialization<commit_after>### define
model : Model
model/route : Route
libs/event_mixin : EventMixin
###

PLANE_XY = 0
PLANE_YZ = 1
PLANE_XZ = 2
VIEW_3D  = 3

TYPE_BRANCH = 1

COLOR_NORMAL = 0xff0000
COLOR_ACTIVE = 0x0000ff
COLOR_BRANCH = 0x550000
COLOR_BRANCH_ACTIVE = 0x000055

class Skeleton

  # This class is supposed to collect all the Geometries that belong to the skeleton, like
  # nodes, edges and trees

  constructor : (maxRouteLen, flycam, model) ->
    _.extend(this, new EventMixin())

    @maxRouteLen  = maxRouteLen
    @flycam       = flycam
    @model        = model
    # Edges
    @routes       = []
    # Nodes
    @nodes        = []
    @nodesSpheres = []
    # Tree IDs
    @ids          = []
    # Current Index
    @curIndex     = []

    # whether or not display the spheres
    @disSpheres   = true

    # Create sphere to represent the active Node, radius is
    # 1 nm, so that @activeNode.scale is the radius in nm.
    @activeNode = new THREE.Mesh(
        new THREE.SphereGeometry(1 / @model.Route.scaleX),
        new THREE.MeshLambertMaterial({
          color : COLOR_ACTIVE
          #transparent: true
          #opacity: 0.5 })
          })
      )
    @activeNode.doubleSided = true

    @reset()

  createNewTree : (treeId) ->
    # create route to show in previewBox and pre-allocate buffers
    routeGeometry = new THREE.Geometry()
    routeGeometryNodes = new THREE.Geometry()
    routeGeometry.dynamic = true
    routeGeometryNodes.dynamic = true

    for i in [1..@maxRouteLen]
      # workaround to hide the unused vertices
      routeGeometry.vertices.push(new THREE.Vector2(0,0))      # sources
      routeGeometry.vertices.push(new THREE.Vector2(0,0))      # targets
      routeGeometryNodes.vertices.push(new THREE.Vector2(0,0)) # nodes

    @routes.push(new THREE.Line(routeGeometry, new THREE.LineBasicMaterial({color: COLOR_NORMAL, linewidth: 1}), THREE.LinePieces))
    @nodes.push(new THREE.ParticleSystem(routeGeometryNodes, new THREE.ParticleBasicMaterial({color: COLOR_NORMAL, size: 5, sizeAttenuation : false})))
    @ids.push(treeId)
    @curIndex.push(0)

    # Initialize the tree
    @clearRoute(treeId)

    @setActiveNode()
    
    @trigger "newGeometries", [@routes[@routes.length - 1], @nodes[@nodes.length - 1]]

  clearRoute : (treeId) ->
    index = @getIndexFromTreeId(treeId)
    for i in [0..@maxRouteLen - 1]
      # workaround to hide the unused vertices
      @routes[index].geometry.vertices[2 * i]     = new THREE.Vector2(0,0)
      @routes[index].geometry.vertices[2 * i + 1] = new THREE.Vector2(0,0)
      @nodes[index].geometry.vertices[i] = new THREE.Vector2(0,0)
      @routes[index].geometry.verticesNeedUpdate = true
      @nodes[index].geometry.verticesNeedUpdate = true
    @curIndex[index] = 0

  reset : ->
    if (@ids.length > 0)
      @trigger "removeGeometries", @routes.concat(@nodes).concat(@nodesSpheres)
    @routes       = []
    @nodes        = []
    @ids          = []
    @nodesSpheres = []
    for tree in @model.Route.getTrees()
      @createNewTree(tree.treeId)
    @loadSkeletonFromModel()
    # Add Spheres to the scene
    @trigger "newGeometries", @nodesSpheres

  loadSkeletonFromModel : ->
    for tree in @model.Route.getTrees()
      nodeList = @model.Route.getNodeList(tree)

      index = @getIndexFromTreeId(tree.treeId)

      # Check that we are not dealing with a sentinel
      if nodeList[0].id
        # Draw first Node, because it has no parent and therefore isn't drawn in the loop below
        if nodeList.length > 0
          radius = nodeList[0].size
          nodePos = nodeList[0].pos
          @nodes[index].geometry.vertices[@curIndex[index]]    = new THREE.Vector3(nodePos[0], nodePos[1], nodePos[2])
          # Assign the ID to the vertex, so we can access it later
          @nodes[index].geometry.vertices[@curIndex[index]].nodeId = nodeList[0].id
          @pushNewNode(radius, nodePos, nodeList[0].id)
        @curIndex[index]++
        for node in nodeList
          if node.parent
            radius = node.size
            nodePos = node.parent.pos
            node2Pos = node.pos
            @routes[index].geometry.vertices[2 * @curIndex[index]]     = new THREE.Vector3(nodePos[0], nodePos[1], nodePos[2])
            @routes[index].geometry.vertices[2 * @curIndex[index] + 1] = new THREE.Vector3(node2Pos[0], node2Pos[1], node2Pos[2])
            @nodes[index].geometry.vertices[@curIndex[index]]    = new THREE.Vector3(node2Pos[0], node2Pos[1], node2Pos[2])
            # Assign the ID to the vertex, so we can access it later
            @nodes[index].geometry.vertices[@curIndex[index]].nodeId = node.id
            @pushNewNode(radius, node2Pos, node.id)
            @curIndex[index]++
        @routes[index].geometry.verticesNeedUpdate = true
        @nodes[index].geometry.verticesNeedUpdate = true
    for branchPoint in @model.Route.branchStack
      @setBranchPoint(true, branchPoint.id)
    @setActiveNode()

  setActiveNode : =>
    id = @model.Route.getActiveNodeId()
    position = @model.Route.getActiveNodePos()
    if @activeNodeSphere and @disSpheres==true
      @activeNodeSphere.visible = true
    # May be null
    @lastNodePosition = position
    if position
      @activeNode.visible = true
      @activeNodeSphere = @getSphereFromId(id)
      # Hide activeNodeSphere, because activeNode is visible anyway
      if @activeNodeSphere
        @activeNodeSphere.visible = false
        if @model.Route.getActiveNodeType() == TYPE_BRANCH
          @activeNode.material.color.setHex(COLOR_BRANCH_ACTIVE)
        else
          @activeNode.material.color.setHex(COLOR_ACTIVE)
      @setNodeRadius(@model.Route.getActiveNodeRadius())
      @activeNode.position = new THREE.Vector3(position[0], position[1], position[2])
    else
      @activeNodeSphere = null
      @activeNode.visible = false
    @flycam.hasChanged = true

  setBranchPoint : (isBranchPoint, nodeID) ->
    colorActive = if isBranchPoint then COLOR_BRANCH_ACTIVE else COLOR_ACTIVE
    colorNormal = if isBranchPoint then COLOR_BRANCH else COLOR_NORMAL
    if not nodeID? or nodeID == @model.Route.getActiveNodeId()
      @activeNode.material.color.setHex(colorActive)
      if @activeNodeSphere
        @activeNodeSphere.material.color.setHex(colorNormal)
    else
      sphere = @getSphereFromId(nodeID)
      sphere.material.color.setHex(colorNormal)
    @flycam.hasChanged = true

  setNodeRadius : (value) ->
{'fraction_non_alphanumeric': 0.10997786211258698, 'fraction_numerical': 0.016366223908918405, 'mean_word_length': 2.889606396063961, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5519832', 'n_tokens_mistral': 4119, 'n_tokens_neox': 3968, 'n_words': 898}