Hello,
We have an app where we need to specify a different width for each column. I have attached an image of the result we want. We are currently using “SCIFastColumnRenderableSeries” for the diagram, but all columns have a fixed width and I couldn’t find any way to modify it.
Do you have any suggestions how to accomplish this?
Thanks!
- Alexandru Motoc asked 4 years ago
- You must login to post comments
Achieved the desired behaviour by implementing our custom renderable series. The key idea is to have the series plotted on a value based X-axis (numeric, date, etc.). The custom renderable series will use the default “SCIXyRenderPassData” to get the X axis tick coordinates. It will then draw the bars from one tick to the next one.
The implementation:
class FlexibleColumnWidthRenderableSeries: SCIXyRenderableSeriesBase {
/// In default constructor we will use:
/// - SCIXyRenderPassData which will store points to draw ( If you need to store some additional data for drawing you can extend it and add additional fields )
/// - SCIPointMarkerHitProvider which performs hit checks on points rendered by series. In our case we just check if point marker is hit
/// - SCINearestXyPointProvider allows to locate nearest (x, y) point to specified point on screen
override init() {
super.init(
renderPassData: SCIXyRenderPassData(),
hitProvider: SCIPointMarkerHitProvider(),
nearestPointProvider: SCINearestXyPointProvider()
)
}
override func internalDraw(
with renderContext: ISCIRenderContext2D!,
assetManager: ISCIAssetManager2D!,
renderPassData: ISCISeriesRenderPassData!) {
guard self.opacity != 0 else { return }
guard let fillProvider = paletteProvider as? ISCIFillPaletteProvider else {
// this can be changed in the future if we want to support `strokeStyle`
assertionFailure("this renderable series expects a fill pallete provider")
return
}
/// The render pass data which we created in constructor
let rdp: SCIXyRenderPassData! = renderPassData as? SCIXyRenderPassData
let rects = getRectsForDrawing(renderPassData: rdp)
rects.enumerated().forEach { index, rect in
let color = fillProvider.fillColors.getValueAt(index)
let brush = assetManager.brush(with: SCISolidBrushStyle(colorCode: color))
renderContext.fill(rect, with: brush)
}
}
private func getRectsForDrawing(renderPassData: SCIXyRenderPassData) -> [CGRect] {
/// Ensures the chart's smallest bar has at least a minimum height
let maxY = getMaxY(forRenderPassData: renderPassData)
func computeRect(startingAtPoint point: Int) -> CGRect {
let xCoord = CGFloat(renderPassData.xCoords.getValueAt(point))
let yCoord = min(CGFloat(renderPassData.yCoords.getValueAt(point)), maxY)
let nextXCoord = CGFloat(renderPassData.xCoords.getValueAt(point + 1))
let height = renderPassData.viewportSize.height - yCoord
let columnWidth = nextXCoord - xCoord
// here we might want to support `zeroLineY` into the computation for Y position
return CGRect(
x: xCoord,
y: yCoord,
width: columnWidth,
height: height
)
}
return (0 ..< renderPassData.pointsCount - 1).map { computeRect(startingAtPoint: $0) }
}
/// Computes the maximum value an Y coordinate can have in the chart such that we don't have one pixel bars.
///
/// SciChart computes the Y coordinates by comparing the data we provide in the `dataSeries`.
/// Therefore, the minimum and maximum Y values in the `dataSeries` will have a corresponding Y coordinate to the top and bottom of the viewport.
/// This is how they are internally computed by SciChart's `SCIXyRenderPassData` which we used in our `init()`.
/// - Parameter rdp: The render pass data we provided in the init
/// - Returns: The max Y coordinate allowed on the chart to ensure we don't have 1 pixel-height columns.
private func getMaxY(forRenderPassData rdp: SCIXyRenderPassData) -> CGFloat {
let yCoords = Set(rdp.yCoords.itemsArray).sorted()
let columnHeightDelta: CGFloat = 1
var maxY = CGFloat(yCoords.last ?? 0)
if rdp.viewportSize.height - maxY <= columnHeightDelta, yCoords.count > 1 {
let nextY = CGFloat(yCoords.suffix(2).first!)
maxY -= (maxY - nextY) / 3
}
return maxY
}
}
- Alexandru Motoc answered 4 years ago
- You must login to post comments
Please login first to submit.