io.data2viz.voronoi.Beach.kt Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2018-2019. data2viz sàrl.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.data2viz.voronoi
import io.data2viz.geom.Point
import kotlin.math.abs
import kotlin.math.sqrt
val epsilon = 1e-6
val epsilon2 = 1e-12
//todo don't use global val. Could be a
internal val beaches = RedBlackTree()
internal val beachPool = mutableListOf>()
internal fun T.redBlackNode(): RedBlackNode = RedBlackNode(this)
class Beach {
lateinit var site: Site
var circleNode: RedBlackNode? = null
val circle: Circle?
get() = circleNode?.node
var edge: Edge? = null
}
val RedBlackNode.site : Site
get() = this.node.site
val RedBlackNode.circle : Circle?
get() = this.node.circle
fun createBeach(site: Site): RedBlackNode {
val beach = if( beachPool.isEmpty()) Beach().redBlackNode() else beachPool.pop()!!
beach.node.site = site
return beach
}
fun detachBeach(beach: RedBlackNode) {
detachCircle(beach)
beaches.remove(beach)
beachPool.add(beach)
beach.clean()
}
fun addBeach(site: Site) {
val x = site.x
val directrix = site.y
var node = beaches.root
var lArc: RedBlackNode? = null
var rArc: RedBlackNode? = null
var dxl: Double
var dxr: Double
while (node != null) {
dxl = leftBreakPoint(node, directrix) - x
if (dxl > epsilon) {
node = node.L
} else {
dxr = x - rightBreakPoint(node, directrix)
if (dxr > epsilon) {
if (node.R == null) {
lArc = node
break
}
node = node.R
} else {
if (dxl > -epsilon) {
lArc = node.P
rArc = node
} else if (dxr > -epsilon) {
lArc = node
rArc = node.N
} else {
lArc = node
rArc = node
}
break
}
}
}
Cell.Companion.createCell(site)
val newArc = createBeach(site)
beaches.insert(newArc, lArc)
if (lArc == null && rArc == null) return
if (lArc === rArc) {
detachCircle(lArc!!)
rArc = createBeach(lArc.site)
beaches.insert(rArc, newArc)
newArc.node.edge = Edge.Companion.createEdge(lArc.site, newArc.site)
rArc.node.edge = newArc.node.edge
attachCircle(lArc)
attachCircle(rArc)
return
}
if (rArc == null){
newArc.node.edge = Edge.Companion.createEdge(lArc!!.site, newArc.site)
return
}
detachCircle(lArc!!)
detachCircle(rArc)
val lSite =lArc.site
val ax = lSite.x
val ay = lSite.y
val bx = site.x - ax
val by = site.y - ay
val rSite = rArc.site
val cx = rSite.x - ax
val cy = rSite.y - ay
val d = 2 * (bx * cy - by * cx)
val hb = bx * by + by * by
val hc = cx * cx + cy * cy
val vertex = Point((cy * hb - by * hc) / d + ax, (bx * hc - cx * hb) / d + ay)
Edge.Companion.setEdgeEnd(rArc.node.edge!!, lSite, rSite, vertex)
newArc.node.edge = Edge.Companion.createEdge(lSite, site, null, vertex)
rArc.node.edge = Edge.Companion.createEdge(site, rSite, null, vertex)
attachCircle(lArc)
attachCircle(rArc)
}
fun removeBeach(circle: RedBlackNode) {
val beach = circle.node.arcNode!!
val x = circle.x //centre du cercle x
val y = circle.cy //centre du cercle y
val vertex = Point(x, y) //le nouveau vertex correspond au centre du cercle supprimé
var previous = beach.P
var next = beach.N
val disappearing = mutableListOf(beach)
detachBeach(beach)
var lArc = previous
while (lArc?.node?.circle != null
&& abs(x - lArc.circle!!.x) < epsilon
&& abs(y - lArc.circle!!.cy) < epsilon) {
previous = lArc.P
disappearing.add(0,lArc)
detachBeach(lArc)
lArc = previous
}
disappearing.add(0, lArc!!)
detachCircle(lArc)
var rArc = next
while (rArc?.node?.circle != null
&& abs(x - rArc.circle!!.x) < epsilon
&& abs(y - rArc.circle!!.cy) < epsilon) {
next = rArc.N
disappearing.add(rArc)
detachBeach(rArc)
rArc = next
}
disappearing.add(rArc!!)
detachCircle(rArc)
val nArcs = disappearing.size
// var iArc = 1
for (iArc in 1..nArcs-1) { // todo why ++iArc
rArc = disappearing[iArc]
lArc = disappearing[iArc - 1]
Edge.Companion.setEdgeEnd(rArc.node.edge!!, lArc.site, rArc.site, vertex)
}
lArc = disappearing[0]
rArc = disappearing[nArcs - 1]
rArc.node.edge = Edge.Companion.createEdge(lArc.site, rArc.site, null, vertex)
attachCircle(lArc)
attachCircle(rArc)
}
fun leftBreakPoint(arc: RedBlackNode, directrix: Double): Double {
var site = arc.node.site
val rfocx = site.x //foyer x ?
val rfocy = site.y //foyer y ?
val pby2 = rfocy - directrix
if (pby2 == 0.0) return rfocx
val lArc = arc.P ?: return Double.NEGATIVE_INFINITY
site = lArc.node.site
val lfocx = site.x
val lfocy = site.y
val plby2 = lfocy - directrix
if (plby2 == 0.0) return lfocx
val hl = lfocx - rfocx
val aby2 = 1 / pby2 - 1 / plby2
val b = hl / plby2
return if (aby2 != 0.0)
(-b + sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx
else
(rfocx + lfocx) / 2
}
fun rightBreakPoint(arc: RedBlackNode, directrix: Double): Double {
val rArc = arc.N
if (rArc != null) return leftBreakPoint(rArc, directrix)
val site = arc.node.site
return if (site.y == directrix) site.x else Double.POSITIVE_INFINITY
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy