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

jvmTest.plot.builder.interact.loc.PolygonEdgeCasesTest.kt Maven / Gradle / Ivy

/*
 * Copyright (c) 2019. 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.builder.interact.loc

import jetbrains.datalore.base.algorithms.splitRings
import jetbrains.datalore.base.geometry.DoubleRectangle
import jetbrains.datalore.base.geometry.DoubleVector
import jetbrains.datalore.base.typedGeometry.Untyped
import jetbrains.datalore.base.typedGeometry.Vec
import jetbrains.datalore.base.typedGeometry.createMultiPolygon
import jetbrains.datalore.base.typedGeometry.explicitVec
import jetbrains.datalore.plot.base.aes.AestheticsBuilder
import jetbrains.datalore.plot.base.aes.AestheticsBuilder.Companion.list
import jetbrains.datalore.plot.base.geom.util.GeomUtil
import jetbrains.datalore.plot.base.geom.util.GeomUtil.rectToGeometry
import jetbrains.datalore.plot.base.geom.util.MultiPointDataConstructor
import jetbrains.datalore.plot.base.geom.util.MultiPointDataConstructor.collector
import jetbrains.datalore.plot.base.geom.util.MultiPointDataConstructor.multiPointAppender
import jetbrains.datalore.plot.base.geom.util.MultiPointDataConstructor.reducer
import jetbrains.datalore.plot.base.geom.util.MultiPointDataConstructor.singlePointAppender
import jetbrains.datalore.plot.base.interact.GeomTargetLocator
import jetbrains.datalore.plot.base.interact.GeomTargetLocator.LookupSpace
import jetbrains.datalore.plot.base.interact.GeomTargetLocator.LookupStrategy
import jetbrains.datalore.plot.builder.interact.TestUtil.assertEmpty
import jetbrains.datalore.plot.builder.interact.TestUtil.assertObjects
import jetbrains.datalore.plot.builder.interact.TestUtil.createLocator
import jetbrains.datalore.plot.builder.interact.TestUtil.first
import jetbrains.datalore.plot.builder.interact.TestUtil.last
import jetbrains.datalore.plot.builder.interact.TestUtil.multipolygon
import jetbrains.datalore.plot.builder.interact.TestUtil.point
import jetbrains.datalore.plot.builder.interact.TestUtil.polygon
import jetbrains.datalore.plot.builder.interact.TestUtil.polygonTarget
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class PolygonEdgeCasesTest {

    private val polygonLocator: GeomTargetLocator
        get() = createLocator(
            LookupStrategy.HOVER, LookupSpace.XY,
            TARGET
        )

    @Test
    fun whenInside_AndControlSegmentGoesThroughSegmentJoints_ShouldFindPolygon() {

        val locator = polygonLocator

        assertObjects(locator, point(X_INSIDE, JOINT.y), POLYGON_KEY)
    }

    @Test
    fun whenOutside_AndControlSegmentGoesThroughSegmentJoints_ShouldNotFindPolygon() {
        val locator = polygonLocator

        assertEmpty(locator, point(X_OUTSIDE_RIGHT, JOINT.y))
    }

    @Test
    fun whenOnTheLeftSide_AndControlSegmentGoesThroughSegmentJoint_ShouldFindPolygon() {
        val locator = polygonLocator

        assertObjects(locator, point(LEFT_COORD, JOINT.y), POLYGON_KEY)
    }

    @Test
    fun whenOnTheRightSide_AndControlSegmentGoesThroughSegmentJoints_ShouldNotFindPolygon() {
        val locator = polygonLocator

        assertEmpty(locator, point(RIGHT_COORD, JOINT.y))
    }

    @Test
    fun whenOnSide_AndControlSegmentLiesOnSegment_ShouldNotFindPolygon() {
        val locator = polygonLocator

        assertEmpty(locator, point(X_OUTSIDE_RIGHT, BOTTOM_COORD))
        assertEmpty(locator, point(X_OUTSIDE_RIGHT, TOP_COORD))
    }

    @Test
    fun whenOutside_AndPointsAreNotClosed_ShouldFindPolygon() {
        val points = listOf(
            point(0.0, 0.0),
            point(0.0, 100.0),
            point(100.0, 100.0)
        )

        val locator = createLocator(LookupStrategy.HOVER, LookupSpace.XY, polygonTarget(POLYGON_KEY, points))
        assertEmpty(locator, point(55.0, 50.0))
        assertObjects(locator, point(40.0, 50.0), POLYGON_KEY)
    }

    @Test
    fun geomUtilSplitRingsFromPath_WhenRegionsAreClosed_ShouldAddRegions() {
        val ring1 = polygon(
            point(0.0, 0.0),
            point(5.0, 5.0),
            point(7.0, 7.0)
        )


        val ring2 = polygon(
            point(10.0, 10.0),
            point(15.0, 15.0),
            point(5.0, 5.0)
        )

        val ring3 = polygon(
            point(45.0, 45.0),
            point(90.0, 90.0),
            point(12.0, 12.0)
        )

        val list = multipolygon(ring1, ring2, ring3)

        val rings = splitRings(list)

        assertEquals(3, rings.size)
        assertEquals(ring1, rings[0])
        assertEquals(ring2, rings[1])
        assertEquals(ring3, rings[2])
    }

    @Test
    fun geomUtilSplitRingsFromPath_WhenRingIsNotClosed_ShouldCloseRing() {
        val openRing = listOf(
            point(0.0, 0.0),
            point(5.0, 5.0),
            point(7.0, 7.0)
        )

        val rings = splitRings(openRing)

        val expectedRing = ArrayList(openRing)
        expectedRing.add(openRing[0])

        assertEquals(1, rings.size)
        assertEquals(expectedRing, rings[0])
    }

    @Test
    fun geomUtilSplitRingsFromPath_WhenPathIsEmpty_ShouldReturnEmptyList() {
        val rings = splitRings(emptyList())

        assertTrue(rings.isEmpty())
    }

    @Test
    fun multiPointDataConstructor_ShouldNotReduceRingControlPoints() {
        val startRing1 = point(0.0, 0.0)
        val startRing2 = point(0.0, 10.0)
        val startRing3 = point(0.5, 10.0)

        val polygon = multipolygon(
            polygon(
                startRing1,
                point(4.0, 0.0)
            ),
            polygon(
                startRing2,
                point(4.0, 10.0),
                point(0.2, 10.0)
            ),
            polygon(
                startRing3,
                point(4.0, 20.0)
            )
        )

        val aes = AestheticsBuilder(polygon.size)
            .x(list(polygon.map { point -> point.x }))
            .y(list(polygon.map { point -> point.y }))
            .build()

        val multiPointDataList = MultiPointDataConstructor.createMultiPointDataByGroup(
            aes.dataPoints(),
            singlePointAppender(GeomUtil.TO_LOCATION_X_Y),
            reducer(1.0, true)
        )
        val rings = splitRings(multiPointDataList[0].points)

        assertEquals(3, rings.size)
        assertRing(rings[0], startRing1)
        assertRing(rings[1], startRing2)
        assertRing(rings[2], startRing3)
    }

    @Test
    fun multiPointDataConstructor_ShouldGroupRectangles() {
        val leftFromAntiMeridian = DoubleRectangle.span(point(120.0, 50.0), point(180.0, 20.0))
        val rightFromAntiMeridian = DoubleRectangle.span(point(-180.0, 50.0), point(-90.0, 20.0))

        val rects = listOf(
            Pair(1, leftFromAntiMeridian),
            Pair(2, DoubleRectangle.span(point(30.0, 40.0), point(20.0, 10.0))),
            Pair(1, rightFromAntiMeridian)
        )

        val aes = AestheticsBuilder(rects.size)
            .xmin { i -> rects[i].second.left }
            .xmax { i -> rects[i].second.right }
            .ymin { i -> rects[i].second.bottom }
            .ymax { i -> rects[i].second.top }
            .group { i -> rects[i].first }
            .build()

        val multiPointDataList = MultiPointDataConstructor.createMultiPointDataByGroup(
            aes.dataPoints(),
            multiPointAppender(GeomUtil.TO_RECTANGLE),
            collector()
        )

        assertEquals(2, multiPointDataList.size)

        val rings = splitRings(multiPointDataList[0].points)
        assertEquals(2, rings.size)
        assertEquals(polygonFromRect(leftFromAntiMeridian), rings[0])
        assertEquals(polygonFromRect(rightFromAntiMeridian), rings[1])
    }

    @Test
    fun geomUtilCreateMultipolygon_fromThreeRing() {
        fun vec(x: Double, y: Double) = explicitVec(x, y)

        val points = ArrayList>()
        points.addAll(
            listOf(
                vec(0.0, 0.0),
                vec(0.0, 50.0),
                vec(50.0, 50.0),
                vec(50.0, 0.0),
                vec(0.0, 0.0)
            )
        )

        points.addAll(
            listOf(
                vec(100.0, 0.0),
                vec(100.0, 50.0),
                vec(150.0, 50.0),
                vec(150.0, 0.0),
                vec(100.0, 0.0)
            )
        )

        points.addAll(
            listOf(
                vec(110.0, 10.0),
                vec(140.0, 10.0),
                vec(140.0, 40.0),
                vec(110.0, 40.0),
                vec(110.0, 10.0)
            )
        )

        val multipolygon = createMultiPolygon(points)

        assertEquals(2, multipolygon.size)
        assertEquals(1, multipolygon[0].size)
        assertEquals(2, multipolygon[1].size)
    }

    private fun assertRing(ring: List, startPoint: DoubleVector) {
        assertEquals(first(ring), last(ring))
        assertEquals(startPoint, first(ring))
    }

    companion object {

        private const val LEFT_COORD = 0.0
        private const val RIGHT_COORD = 200.0
        private const val X_INSIDE = 50.0
        private const val X_OUTSIDE_RIGHT = 300.0

        private const val TOP_COORD = 200.0
        private const val BOTTOM_COORD = 0.0
        private val JOINT = DoubleVector(100.0, 100.0)

        /*
    200 *-------*
        |       |
    100 *       *
        |       |
      0 *-------*
        0   100 200
  */

        private val POLYGON = polygon(
            point(LEFT_COORD, BOTTOM_COORD),
            point(LEFT_COORD, JOINT.y),
            point(LEFT_COORD, TOP_COORD),
            point(RIGHT_COORD, TOP_COORD),
            point(RIGHT_COORD, JOINT.y),
            point(RIGHT_COORD, BOTTOM_COORD)
        )


        private const val POLYGON_KEY = 1
        private val TARGET = polygonTarget(POLYGON_KEY, POLYGON)

        private fun polygonFromRect(rect: DoubleRectangle): List {
            return rectToGeometry(rect.left, rect.bottom, rect.right, rect.top)
        }
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy