All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.jetbrains.datalore.plot.base.geom.PieGeom.kt Maven / Gradle / Ivy

/*
 * Copyright (c) 2020. JetBrains s.r.o.
 * Use of this source code is governed by the MIT license that can be found in the LICENSE file.
 */

package jetbrains.datalore.plot.base.geom

import jetbrains.datalore.base.gcommon.collect.Iterables
import jetbrains.datalore.base.gcommon.collect.Iterables.get
import jetbrains.datalore.base.geometry.DoubleVector
import jetbrains.datalore.plot.base.*
import jetbrains.datalore.plot.base.geom.util.GeomUtil
import jetbrains.datalore.plot.base.geom.util.LinesHelper
import jetbrains.datalore.plot.base.render.SvgRoot
import jetbrains.datalore.plot.base.render.svg.LinePath
import jetbrains.datalore.vis.svg.SvgPathDataBuilder
import kotlin.math.PI

/**
 * This is working code but wasn't included to ggplot.
 * Mostly exists to preserve this code.
 */
internal class PieGeom(private val myCenter: DoubleVector, private val myRadius: Double) : GeomBase() {

    override fun buildIntern(root: SvgRoot, aesthetics: Aesthetics, pos: PositionAdjustment, coord: CoordinateSystem, ctx: GeomContext) {
        val helper = PieHelper(pos, coord, ctx)
        val segments = helper.createSegments(aesthetics, myCenter, myRadius)
        appendNodes(segments, root)
    }

    private class PieHelper internal constructor(pos: PositionAdjustment, coord: CoordinateSystem, ctx: GeomContext) : LinesHelper(pos, coord, ctx) {

        internal fun createSegments(aesthetics: Aesthetics, center: DoubleVector, radius: Double): List {
            val result = ArrayList()

            //DoubleVector basis = new Point(radius, 0);  // x
            val basis = DoubleVector(0.0, -radius)   // y

            var curAngle = Double.NaN
            for (p in dataPoints(aesthetics)) {
                val segmentSize =
                    shareToRad(p.y()!!)

                // for better presentation: first segment lays to the right of 12 o'clock, and all
                // segments are added counterclockwise.
                if (curAngle.isNaN()) {
                    curAngle = segmentSize
                }

                // we use negative angles because of screen coordinates and counterclockwise order of segments
                val angle = -segmentSize

                val builder = SvgPathDataBuilder(true)
                builder.moveTo(center)
                builder.lineTo(center.add(basis.rotate(curAngle)))
                val arcTo = center.add(basis.rotate(curAngle + angle))
                builder.ellipticalArc(radius, radius, 0.0, -angle > PI, false, arcTo)
                builder.closePath()

                curAngle += angle

                val path = LinePath(builder)
                decorate(path, p, true)
                result.add(path)
            }

            return result
        }
    }

    companion object {
        val RENDERS = listOf(
                Aes.X, // optional, can specify order of segments in pie
                Aes.Y, // angle width of segments
                Aes.COLOR,
                Aes.FILL,
                Aes.ALPHA,
                Aes.WIDTH,
                Aes.SIZE
        )

        const val HANDLES_GROUPS = false

        private fun dataPoints(aesthetics: Aesthetics): Iterable {
            val withX = GeomUtil.with_X_Y(aesthetics.dataPoints())
            return if (Iterables.isEmpty(withX) || allEqualX(
                    withX,
                    get(withX, 0).x()
                )
            ) {
                GeomUtil.ordered_Y(GeomUtil.with_Y(aesthetics.dataPoints()), true)
            } else GeomUtil.ordered_X(withX)
        }

        private fun allEqualX(hasX: Iterable, `val`: Double?): Boolean {
            for (p in hasX) {
                if (p.x() != `val`) {
                    return false
                }
            }
            return true
        }

        private fun shareToRad(share: Double): Double {
            // don't allow sum to be more than 99.99 % of full circle (otherwise arc will disappear)
            return 0.9999 * 2.0 * PI * share
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy