Skip to content

Commit

Permalink
Merge branch 'cpu-event-graphs'
Browse files Browse the repository at this point in the history
  • Loading branch information
bric3 committed Apr 8, 2024
2 parents 2020bcf + fa19143 commit 3697eca
Show file tree
Hide file tree
Showing 15 changed files with 991 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Fireplace
*
* Copyright (c) 2021, Today - Brice Dutheil
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package io.github.bric3.fireplace.charts

import java.awt.Color


fun Color.withAlpha(alpha: Float) = Color(
red,
green,
blue,
(alpha * 255).toInt()
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* Fireplace
*
* Copyright (c) 2021, Today - Brice Dutheil
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package io.github.bric3.fireplace.charts

import io.github.bric3.fireplace.charts.ChartSpecification.LineRendererDescriptor
import io.github.bric3.fireplace.charts.ChartSpecification.RendererDescriptor
import java.awt.Color
import java.awt.Graphics2D
import java.awt.RenderingHints
import java.awt.geom.Rectangle2D
import java.beans.PropertyChangeListener
import java.beans.PropertyChangeSupport

/**
* A chart that can be inlaid into a small space. Generally a chart will render a single dataset, but it is
* also possible to overlay multiple renderer/dataset pairs in the same space.
*/
class Chart : RectangleContent {
private val propertyChangeSupport = PropertyChangeSupport(this)

/**
* A list of cart specifications.
*
* @see ChartSpecification
*/
var chartSpecifications: List<ChartSpecification> = emptyList()
set(value) {
val oldChartDatasetDescriptor = field
if (oldChartDatasetDescriptor == value) {
return
}

field = value
propertyChangeSupport.firePropertyChange("charDatasetDescriptors", oldChartDatasetDescriptor, value)
}

/**
* A background painter for the chart, possibly `null`.
*/
var background: RectangleContent? = null
set(value) {
val oldBackground = field
if (oldBackground == value) {
return
}
field = value
propertyChangeSupport.firePropertyChange("background", oldBackground, value)
}

/**
* The insets (applied after the background has been drawn).
*/
var insets: RectangleMargin = RectangleMargin(2.0, 2.0, 2.0, 2.0)
set(value) {
val oldInsets = field
if (oldInsets == field) {
return
}
field = value
propertyChangeSupport.firePropertyChange("insets", oldInsets, value)
}

/**
* The insets for the plot area (defaults to zero but can be modified to add space for
* annotations etc).
*/
var plotInsets: RectangleMargin = RectangleMargin(0.0, 0.0, 0.0, 0.0)
set(value) {
val oldPlotInsets = field
if (oldPlotInsets == value) {
return
}

field = value
propertyChangeSupport.firePropertyChange("plotInsets", oldPlotInsets, value)
}

/**
* Creates a new chart with the given specifications, dataset and renderer.
*
* @param chartSpecifications The chart specifications
*/
constructor(chartSpecifications: List<ChartSpecification>) {
// TODO how to ensure consistent ranges across multiple charts?
this.chartSpecifications = chartSpecifications
}

fun addPropertyChangeListener(listener: PropertyChangeListener?) {
propertyChangeSupport.addPropertyChangeListener(listener)
}

fun removePropertyChangeListener(listener: PropertyChangeListener?) {
propertyChangeSupport.removePropertyChangeListener(listener)
}

/**
* Draws the chart to a Java2D graphics target.
*
* @param g2 the graphics target (`null` not permitted).
* @param bounds the bounds within which the chart should be drawn.
*/
override fun draw(g2: Graphics2D, bounds: Rectangle2D) {
// set up any rendering hints we want (should allow this to be controlled externally)
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)

if (background != null) {
background!!.draw(g2, bounds)
}

// handle background, margin, border, insets and fill
insets.applyInsets(bounds)

val plotArea = plotInsets.shrink(bounds)

chartSpecifications.forEach {
// plot its dataset in the inner bounds
configureRenderer(it.renderer).draw(this, it.dataset, g2, plotArea)
}
}

private val lineChartRenderer = LineChartRenderer()

private fun configureRenderer(rendererSpec: RendererDescriptor): ChartRenderer {
return when (rendererSpec) {
is LineRendererDescriptor -> lineChartRenderer.apply {
linePaint = rendererSpec.lineColor ?: Color.BLACK
fillColors = rendererSpec.fillColors
}

else -> error("Unsupported render spec")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Fireplace
*
* Copyright (c) 2021, Today - Brice Dutheil
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package io.github.bric3.fireplace.charts

import java.awt.Graphics
import java.awt.Graphics2D
import java.awt.Rectangle
import java.beans.PropertyChangeSupport
import javax.swing.*

/**
* A component that draws an [Chart] within the bounds of the component.
*
* @param chart the chart.
*/
class ChartComponent(chart: Chart? = null) : JComponent() {
private val propertyChangeSupport = PropertyChangeSupport(this)

var chart: Chart? = chart
set(value) {
val oldChart = field
if (oldChart == value) {
return
}
field = value
propertyChangeSupport.firePropertyChange("chart", oldChart, value)
}

/** A reusable rectangle to avoid creating work for the garbage collector. */
private val rect = Rectangle()

init {
border = null
propertyChangeSupport.addPropertyChangeListener("chart") {
revalidate()
repaint()
}
}

/**
* Paints the component. The chart will be drawn at a size matching the
* bounds of the component.
*
* @param g the Java2D graphics target.
*/
override fun paintComponent(g: Graphics) {
super.paintComponent(g)
chart?.let {
val g2 = g as Graphics2D
getBounds(rect)
g.translate(-rect.x, -rect.y)
it.draw(g2, rect)
g.translate(rect.x, rect.y)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Fireplace
*
* Copyright (c) 2021, Today - Brice Dutheil
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package io.github.bric3.fireplace.charts

import java.awt.Graphics2D
import java.awt.geom.Rectangle2D

/**
* A renderer for an [Chart] that draws data with a particular representation
* (for example, lines or bars).
*
* @param background the background (`null` permitted).
*/
abstract class ChartRenderer @JvmOverloads constructor(private val background: RectangleContent? = null) {
/**
* Draws a representation of the supplied dataset within the plot bounds of the supplied
* Java2D graphics target. The chart can be used to provide some global attributes for the
* rendering (such as the x-range and y-range for display).
*
* @param chart the chart.
* @param dataset the dataset.
* @param g2 the Java2D graphics target.
* @param plotBounds the plot bounds.
*/
open fun draw(chart: Chart, dataset: XYDataset, g2: Graphics2D, plotBounds: Rectangle2D) {
if (this.background != null) {
background.draw(g2, plotBounds)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Fireplace
*
* Copyright (c) 2021, Today - Brice Dutheil
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package io.github.bric3.fireplace.charts

import java.awt.Color

/**
* Specifies the various properties of a chart (dataset, label, how it's rendered).
*/
data class ChartSpecification(
val dataset: XYDataset,
val label: String,
val renderer: RendererDescriptor,
) {
/**
* Common interface for specifying how chart is rendered.
*/
sealed interface RendererDescriptor

/**
* Specifies a **line-rendered** chart.
*/
data class LineRendererDescriptor(
val lineColor: Color? = null,
val fillColors: List<Color>? = null,
) : RendererDescriptor
}
Loading

0 comments on commit 3697eca

Please sign in to comment.