Skip to content

Commit

Permalink
Added rendering of polygon shapes as a part of Mil-Std-2525 tactical …
Browse files Browse the repository at this point in the history
…graphics.
  • Loading branch information
ComBatVision committed Nov 15, 2023
1 parent 3c4cf4f commit 73e8410
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 151 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -256,21 +256,22 @@ open class PathsPolygonsLabelsActivity: GeneralGlobeActivity() {
val featureBegin = line.indexOf(wktStart) + wktStart.length
val featureEnd = line.indexOf(wktEnd, featureBegin) + wktEnd.length
val feature = line.substring(featureBegin, featureEnd)
val attributes = line.substring(featureEnd + 1)
val fields = attributes.split(",")
val polygon = Polygon()
polygon.altitudeMode = AltitudeMode.CLAMP_TO_GROUND
polygon.pathType = PathType.LINEAR
polygon.isFollowTerrain = true // essential for preventing long segments from intercepting ellipsoid.
polygon.displayName = fields[1]
polygon.attributes = ShapeAttributes(commonAttrs)
polygon.attributes.interiorColor = Color(
red = random.nextFloat(),
green = random.nextFloat(),
blue = random.nextFloat(),
alpha = 0.3f
)
polygon.highlightAttributes = highlightAttrs
val fields = line.substring(featureEnd + 1).split(",")
val polygon = Polygon().apply {
altitudeMode = AltitudeMode.CLAMP_TO_GROUND
pathType = PathType.LINEAR
isFollowTerrain = true // essential for preventing long segments from intercepting ellipsoid.
displayName = fields[1]
attributes = ShapeAttributes(commonAttrs).apply {
interiorColor = Color(
red = random.nextFloat(),
green = random.nextFloat(),
blue = random.nextFloat(),
alpha = 0.3f
)
}
highlightAttributes = highlightAttrs
}

// Process all the polygons within this feature by creating "boundaries" for each.
// Individual polygons are bounded by "(" and ")"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class PolygonsTutorial(private val engine: WorldWind) : AbstractTutorial() {
// Create an extruded polygon with an inner hole and custom attributes that display the extruded vertical lines,
// make the extruded interior 50% transparent, and increase the polygon line with.
addRenderable(
Polygon(emptyList()).apply {
Polygon().apply {
attributes.apply {
isDrawVerticals = true // display the extruded verticals
interiorColor = Color(1f, 1f, 1f, 0.5f) // 50% transparent white
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import armyc2.c2sd.JavaRendererServer.RenderMultipoints.clsRenderer
import armyc2.c2sd.graphics2d.BasicStroke
import armyc2.c2sd.graphics2d.Point2D
import armyc2.c2sd.renderer.utilities.*
import earth.worldwind.geom.AltitudeMode
import earth.worldwind.geom.Angle.Companion.degrees
import earth.worldwind.geom.Location
import earth.worldwind.geom.Position
Expand Down Expand Up @@ -59,7 +58,7 @@ actual open class MilStd2525TacticalGraphic actual constructor(
if (this::pointUL.isInitialized) pointUL.setLocation(left, top) else pointUL = Point2D.Double(left, top)
}

override fun makeRenderables(scale: Double, shapes: MutableList<Renderable>) {
override fun makeRenderables(scale: Double): List<Renderable> {
val ipc = PointConverter3D(pointUL.x, pointUL.y, scale * 96.0 * 39.3700787)

// // Calculate clipping rectangle
Expand Down Expand Up @@ -88,22 +87,25 @@ actual open class MilStd2525TacticalGraphic actual constructor(
clsRenderer.renderWithPolylines(mss, ipc, null /*rect*/)

// Create Renderables based on Poly-lines and Modifiers from Renderer
for (i in mss.symbolShapes.indices) convertShapeToRenderables(mss.symbolShapes[i], mss, ipc, shapes)
for (i in mss.modifierShapes.indices) convertShapeToRenderables(mss.modifierShapes[i], mss, ipc, shapes)
val lines = mutableListOf<Renderable>()
val outlines = mutableListOf<Renderable>()
for (i in mss.symbolShapes.indices) convertShapeToRenderables(mss.symbolShapes[i], mss, ipc, lines, outlines)
for (i in mss.modifierShapes.indices) convertShapeToRenderables(mss.modifierShapes[i], mss, ipc, lines, outlines)
invalidateExtent() // Regenerate extent in next frame due to sector may be extended by real shape measures
return outlines + lines
}

protected open fun convertShapeToRenderables(
shape: ShapeInfo, mss: MilStdSymbol, ipc: IPointConversion, shapes: MutableList<Renderable>
si: ShapeInfo, mss: MilStdSymbol, ipc: IPointConversion, lines: MutableList<Renderable>, outlines: MutableList<Renderable>
) {
when (shape.shapeType) {
ShapeInfo.SHAPE_TYPE_POLYLINE -> {
when (si.shapeType) {
ShapeInfo.SHAPE_TYPE_POLYLINE, ShapeInfo.SHAPE_TYPE_FILL -> {
val shapeAttributes = ShapeAttributes().apply {
outlineWidth = MilStd2525.graphicsLineWidth
shape.lineColor?.let { outlineColor = Color(it.toARGB()) }
shape.fillColor?.let { interiorColor = Color(it.toARGB()) }
(si.lineColor ?: si.fillColor)?.let { outlineColor = Color(it.toARGB()) }
(si.fillColor ?: si.lineColor)?.let { interiorColor = Color(it.toARGB()) }
// Fill dash pattern
val stroke = shape.stroke
val stroke = si.stroke
if (stroke is BasicStroke) {
val dash = stroke.dashArray
if (dash != null && dash.isNotEmpty()) outlineImageSource = ImageSource.fromLineStipple(
Expand All @@ -114,13 +116,11 @@ actual open class MilStd2525TacticalGraphic actual constructor(
}
val hasOutline = MilStd2525.graphicsOutlineWidth != 0f
val outlineAttributes = if (hasOutline) ShapeAttributes(shapeAttributes).apply {
outlineColor = Color(RendererUtilities.getIdealOutlineColor(shape.lineColor).toARGB()).apply { alpha = 0.5f }
outlineColor = Color(RendererUtilities.getIdealOutlineColor(si.lineColor ?: si.fillColor).toARGB()).apply { alpha = 0.5f }
outlineWidth += MilStd2525.graphicsOutlineWidth * 2f
} else shapeAttributes
val lines = mutableListOf<Renderable>()
val outlines = mutableListOf<Renderable>()
for (idx in shape.polylines.indices) {
val polyline = shape.polylines[idx]
for (idx in si.polylines.indices) {
val polyline = si.polylines[idx]
val positions = mutableListOf<Position>()
for (p in polyline.indices) {
val geoPoint = ipc.PixelsToGeo(polyline[p])
Expand All @@ -129,23 +129,19 @@ actual open class MilStd2525TacticalGraphic actual constructor(
sector.union(position) // Extend bounding box by real graphics measures
}
for (i in 0..1) {
val path = Path(positions, if (i == 0) shapeAttributes else outlineAttributes).apply {
altitudeMode = AltitudeMode.CLAMP_TO_GROUND
isFollowTerrain = true
maximumIntermediatePoints = 0 // Do not draw intermediate vertices for tactical graphics
highlightAttributes = ShapeAttributes(attributes).apply {
outlineWidth *= HIGHLIGHT_FACTOR
}
pickDelegate = this@MilStd2525TacticalGraphic
val shape = if (si.shapeType == ShapeInfo.SHAPE_TYPE_FILL) {
Polygon(positions, if (i == 0) shapeAttributes else outlineAttributes)
} else {
Path(positions, if (i == 0) shapeAttributes else outlineAttributes)
}
if (i == 0) lines += path else outlines += path
applyShapeAttributes(shape)
if (i == 0) lines += shape else outlines += shape
if (!hasOutline) break
}
}
shapes += outlines
shapes += lines
}
ShapeInfo.SHAPE_TYPE_MODIFIER_FILL -> {

ShapeInfo.SHAPE_TYPE_MODIFIER, ShapeInfo.SHAPE_TYPE_MODIFIER_FILL -> {
val rs = RendererSettings.getInstance()
val textAttributes = TextAttributes().apply {
textColor = Color(mss.lineColor.toARGB())
Expand All @@ -155,16 +151,12 @@ actual open class MilStd2525TacticalGraphic actual constructor(
)
outlineWidth = MilStd2525.graphicsOutlineWidth
}
val point = ipc.PixelsToGeo(shape.modifierStringPosition ?: shape.glyphPosition)
val point = ipc.PixelsToGeo(si.modifierStringPosition ?: si.glyphPosition)
val position = Position.fromDegrees(point.y, point.x, 0.0)
sector.union(position) // Extend bounding box by real graphics measures
val label = Label(position, shape.modifierString, textAttributes).apply {
altitudeMode = AltitudeMode.CLAMP_TO_GROUND
rotation = shape.modifierStringAngle.degrees
rotationMode = OrientationMode.RELATIVE_TO_GLOBE
pickDelegate = this@MilStd2525TacticalGraphic
}
shapes += label
val label = Label(position, si.modifierString, textAttributes)
applyLabelAttributes(label, si.modifierStringAngle.degrees)
lines += label
}
else -> Logger.logMessage(Logger.ERROR, "MilStd2525TacticalGraphic", "convertShapeToRenderables", "unknownShapeType")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import earth.worldwind.render.Texture
import kotlin.jvm.JvmOverloads
import kotlin.math.sqrt

abstract class AbstractShape @JvmOverloads constructor(
override var attributes: ShapeAttributes = ShapeAttributes()
): AbstractRenderable(), Attributable, Highlightable {
abstract class AbstractShape(override var attributes: ShapeAttributes): AbstractRenderable(), Attributable, Highlightable {
companion object {
const val NEAR_ZERO_THRESHOLD = 1.0e-10
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import earth.worldwind.util.kgl.*
import kotlin.jvm.JvmOverloads

open class Polygon @JvmOverloads constructor(
positions: List<Position> = listOf(), attributes: ShapeAttributes = ShapeAttributes()
positions: List<Position> = emptyList(), attributes: ShapeAttributes = ShapeAttributes()
): AbstractShape(attributes) {
protected val boundaries = mutableListOf(positions)
val boundaryCount get() = boundaries.size
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import earth.worldwind.geom.*
import earth.worldwind.render.AbstractSurfaceRenderable
import earth.worldwind.render.RenderContext
import earth.worldwind.render.Renderable
import earth.worldwind.shape.Highlightable
import earth.worldwind.shape.*
import earth.worldwind.util.Logger
import kotlin.jvm.JvmStatic
import kotlin.math.PI
Expand All @@ -28,8 +28,7 @@ abstract class AbstractMilStd2525TacticalGraphic(
}
private var minScale = 0.0
private var maxScale = 0.0
private val lodBuffer = mutableMapOf<Int,MutableList<Renderable>>()
private var shapes: MutableList<Renderable>? = null
private val lodBuffer = mutableMapOf<Int, List<Renderable>>()

protected companion object {
const val MAX_WIDTH_DP = 0.0005
Expand Down Expand Up @@ -78,23 +77,17 @@ abstract class AbstractMilStd2525TacticalGraphic(
override fun doRender(rc: RenderContext) {
val terrainSector = rc.terrain.sector
if (!terrainSector.isEmpty && terrainSector.intersects(sector) && getExtent(rc).intersectsFrustum(rc.frustum)) {
// Use shapes from previous frame during pick
if (!rc.isPickMode) {
// Get current map scale based on observation range.
var currentScale = computeScale(rc)
// Limit scale based on clipping sector diagonal size
if (currentScale < minScale) currentScale = minScale
else if (currentScale > maxScale) currentScale = maxScale
// Get renderables for current LoD
val equatorialRadius = rc.globe.equatorialRadius
val lod = computeNearestLoD(equatorialRadius, currentScale)
shapes = lodBuffer[lod] ?: mutableListOf<Renderable>().also {
lodBuffer[lod] = it
makeRenderables(computeLoDScale(equatorialRadius, lod), it)
}
}
// Get current map scale based on observation range.
var currentScale = computeScale(rc)
// Limit scale based on clipping sector diagonal size
if (currentScale < minScale) currentScale = minScale
else if (currentScale > maxScale) currentScale = maxScale
// Get renderables for current LoD
val equatorialRadius = rc.globe.equatorialRadius
val lod = computeNearestLoD(equatorialRadius, currentScale)
val shapes = lodBuffer[lod] ?: makeRenderables(computeLoDScale(equatorialRadius, lod)).also { lodBuffer[lod] = it }
// Draw available shapes
shapes?.forEach { renderable ->
for (renderable in shapes) {
if (renderable is Highlightable) renderable.isHighlighted = isHighlighted
renderable.render(rc)
}
Expand All @@ -110,11 +103,25 @@ abstract class AbstractMilStd2525TacticalGraphic(
maxScale = diagonalDistance / MIN_WIDTH_DP
}

protected open fun reset() {
lodBuffer.clear()
shapes = null
}
protected open fun reset() = lodBuffer.clear()

abstract fun transformLocations(locations: List<Location>)
abstract fun makeRenderables(scale: Double, shapes: MutableList<Renderable>)
abstract fun makeRenderables(scale: Double): List<Renderable>

protected fun applyShapeAttributes(shape: AbstractShape) = shape.apply {
altitudeMode = AltitudeMode.CLAMP_TO_GROUND
isFollowTerrain = true
maximumIntermediatePoints = 0 // Do not draw intermediate vertices for tactical graphics
highlightAttributes = ShapeAttributes(attributes).apply {
outlineWidth *= HIGHLIGHT_FACTOR
}
pickDelegate = this@AbstractMilStd2525TacticalGraphic
}

protected fun applyLabelAttributes(label: Label, angle: Angle) = label.apply {
altitudeMode = AltitudeMode.CLAMP_TO_GROUND
rotation = angle
rotationMode = OrientationMode.RELATIVE_TO_GLOBE
pickDelegate = this@AbstractMilStd2525TacticalGraphic
}
}
Loading

0 comments on commit 73e8410

Please sign in to comment.