【问题标题】:Implement a custom force in Prefuse's ForceDirectedLayout在 Prefuse 的 ForceDirectedLayout 中实现自定义力
【发布时间】:2015-05-06 10:12:30
【问题描述】:

我想将自定义力添加到 Prefuse 中的力导向布局。具体来说,我想让 a) 特定节点之间的边非常刚性,b) 引入有向边,以便源顶点倾向于在目标顶点上方移动。

任何线索如何进行?

【问题讨论】:

    标签: visualization prefuse


    【解决方案1】:

    可以对标准SpringForce 进行子类化并添加更多约束。以下效果很好:

    import prefuse.util.force.{Spring, SpringForce}
    
    object MySpringForce {
      private final val pi    = math.Pi.toFloat
      private final val piH   = (math.Pi/2).toFloat
      private final val eps   = (1 * math.Pi/180).toFloat
    }
    class MySpringForce extends SpringForce {
      import MySpringForce._
    
      private val TORQUE    = params.length
      private val DISTANCE  = TORQUE + 1
    
      params    = params    ++ Array[Float](5e-5f, -1f)
      minValues = minValues ++ Array[Float](0f   , -1f)
      maxValues = maxValues ++ Array[Float](1e-3f, 500f)
    
      override def getParameterNames: Array[String] =
        super.getParameterNames ++ Array("Torque", "Limit")
    
      private def angleBetween(a: Float, b: Float): Float = {
        val d = b - a
        math.atan2(math.sin(d), math.cos(d)).toFloat
      }
    
      override def getForce(s: Spring): Unit = {
        val item1   = s.item1
        val item2   = s.item2
        val length  = if (s.length < 0) 
          params(SpringForce.SPRING_LENGTH) else s.length
        val x1      = item1.location(0)
        val y1      = item1.location(1)
        val x2      = item2.location(0)
        val y2      = item2.location(1)
        var dx      = x2 - x1
        var dy      = y2 - y1
        val r0      = math.sqrt(dx * dx + dy * dy).toFloat
        val r1      = if (r0 == 0.0) {
          dx  = (math.random.toFloat - 0.5f) / 50.0f
          dy  = (math.random.toFloat - 0.5f) / 50.0f
          math.sqrt(dx * dx + dy * dy).toFloat
        } else r0
        val dist    = params(DISTANCE)
        val r       = if (dist < 0) r1 else math.min(dist, r1)
    
        val d       = r - length
        val coeff   = (if (s.coeff < 0) 
          params(SpringForce.SPRING_COEFF) else s.coeff) * d / r
        item1.force(0) +=  coeff * dx
        item1.force(1) +=  coeff * dy
        item2.force(0) += -coeff * dx
        item2.force(1) += -coeff * dy
    
        val ang = math.atan2(dy, dx).toFloat
        val da = angleBetween(ang, -piH)
        if (math.abs(da) <= eps) return
    
        val af  = da / pi * params(TORQUE)
        val rH  = r/2
        val cx  = (x1 + x2) / 2
        val cy  = (y1 + y2) / 2
        val cos = math.cos(ang + af).toFloat * rH
        val sin = math.sin(ang + af).toFloat * rH
        val x1t = cx - cos
        val y1t = cy - sin
        val x2t = cx + cos
        val y2t = cy + sin
    
        item1.force(0) += x1t - x1
        item1.force(1) += y1t - y1
        item2.force(0) += x2t - x2
        item2.force(1) += y2t - y2
      }
    }
    

    【讨论】:

    • 等效于Math.hypot() 可用吗?
    • @trashgod - 是的,在 Scala 中是math.hypot(x, y)。我的三角函数有点生疏了。
    猜你喜欢
    • 1970-01-01
    • 2020-04-12
    • 2016-10-28
    • 2010-11-09
    • 2012-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多