From 9f0e8fec0bd67bc61b995ade71ae3d036aa012fd Mon Sep 17 00:00:00 2001 From: Jefferson Santos Date: Wed, 19 Feb 2025 10:18:22 -0300 Subject: [PATCH] Extract a common interface for scatter and line chart to allow composite charts using both types --- .../GtPlotterLineChart.class.st | 220 +-------------- .../GtPlotterScatterChart.class.st | 175 +----------- .../GtPlotterXYChart.class.st | 263 ++++++++++++++++++ 3 files changed, 271 insertions(+), 387 deletions(-) create mode 100644 src/GToolkit-Plotter/GtPlotterXYChart.class.st diff --git a/src/GToolkit-Plotter/GtPlotterLineChart.class.st b/src/GToolkit-Plotter/GtPlotterLineChart.class.st index 54858bad7..0e42b2e30 100644 --- a/src/GToolkit-Plotter/GtPlotterLineChart.class.st +++ b/src/GToolkit-Plotter/GtPlotterLineChart.class.st @@ -10,29 +10,7 @@ I draw line charts. " Class { #name : #GtPlotterLineChart, - #superclass : #GtPlotterBuilder, - #instVars : [ - 'data', - 'valueX', - 'scaleX', - 'scaleY', - 'valueY', - 'minX', - 'maxX', - 'minY', - 'maxY', - 'titleX', - 'titleY', - 'ticksX', - 'ticksY', - 'projectionXLabelElement', - 'projectionXValues', - 'projectionXLabelStyle', - 'projectionXLineStyle', - 'labelFormatX', - 'labelFormatY', - 'isAlreadyInitialized' - ], + #superclass : #GtPlotterXYChart, #category : #'GToolkit-Plotter-Builder - Line Chart' } @@ -59,15 +37,6 @@ GtPlotterLineChart >> axisYStencil [ scale: self scaleY ] -{ #category : #'api - instantiation' } -GtPlotterLineChart >> contentStencil [ - ^ [ | anElement | - self initializeScales. - anElement := self newContentElement. - self styleChartElement: anElement. - anElement ] asStencil -] - { #category : #'api - instantiation' } GtPlotterLineChart >> create [ @@ -108,11 +77,6 @@ GtPlotterLineChart >> create [ ^ aContainer ] -{ #category : #'api - data' } -GtPlotterLineChart >> data [ - ^ data -] - { #category : #initialization } GtPlotterLineChart >> initialize [ super initialize. @@ -179,60 +143,6 @@ GtPlotterLineChart >> initializeScales [ ] -{ #category : #'api - axes' } -GtPlotterLineChart >> labelFormatX: aBlock [ - "Create a {{gtClass:BlText}}. - Block has one argument [ :aValue | ... ]" - labelFormatX := aBlock -] - -{ #category : #'api - axes' } -GtPlotterLineChart >> labelFormatY: aBlock [ - "Create a {{gtClass:BlText}}. - Block has one argument [ :aValue | ... ]" - labelFormatY := aBlock -] - -{ #category : #accessing } -GtPlotterLineChart >> maxX [ - ^ maxX -] - -{ #category : #accessing } -GtPlotterLineChart >> maxX: anObject [ - maxX := anObject -] - -{ #category : #accessing } -GtPlotterLineChart >> maxY [ - ^ maxY -] - -{ #category : #accessing } -GtPlotterLineChart >> maxY: anObject [ - maxY := anObject -] - -{ #category : #accessing } -GtPlotterLineChart >> minX [ - ^ minX -] - -{ #category : #accessing } -GtPlotterLineChart >> minX: anObject [ - minX := anObject -] - -{ #category : #accessing } -GtPlotterLineChart >> minY [ - ^ minY -] - -{ #category : #accessing } -GtPlotterLineChart >> minY: anObject [ - minY := anObject -] - { #category : #'instance creation' } GtPlotterLineChart >> newAxisXElement [ | aContainer | @@ -426,134 +336,6 @@ GtPlotterLineChart >> newNoDataElement [ c grid vertical alignCenter ] ] -{ #category : #'instance creation' } -GtPlotterLineChart >> newProjectionXLabelsElement [ - | anElement | - anElement := GtPlotterHorizontalValueProjectionsElement new - scale: self scaleX; - scaleData: valueX; - clipChildren: false; - constraintsDo: [ :c | c vertical fitContent ]; - hideOverlapping; - values: (projectionXValues cull: self data). - - projectionXLabelElement ifNotNil: [ - anElement valueElement: projectionXLabelElement ]. - - projectionXLabelStyle ifNotNil: [ - anElement valueStyle: projectionXLabelStyle ]. - - ^ anElement -] - -{ #category : #'instance creation' } -GtPlotterLineChart >> newProjectionXLinesElement [ - ^ GtPlotterHorizontalValueProjectionsElement new - scale: self scaleX; - scaleData: valueX; - valueStyle: projectionXLineStyle; - values: (projectionXValues cull: self data) -] - -{ #category : #'api - instantiation' } -GtPlotterLineChart >> projectionLabelXStencil [ - (self data size isZero or: [ projectionXValues isNil ]) - ifTrue: [ ^ nil ]. - self initializeScales. - ^ BrValuableStencil new - valuable: [ self newProjectionXLabelsElement ] -] - -{ #category : #'api - projections' } -GtPlotterLineChart >> projectionXLabelElement: aBlock [ - "Return projection label element. - Block has one argument: [ :aGtPlotterSingleScaleContext | ... ]" - projectionXLabelElement := aBlock -] - -{ #category : #'api - projections' } -GtPlotterLineChart >> projectionXLabelStyle: aBlock [ - "Style a label element. - Block has one argument: [ :anElement | ... ]" - projectionXLabelStyle := aBlock -] - -{ #category : #'api - projections' } -GtPlotterLineChart >> projectionXLineStyle: aBlock [ - "Style a label element. - Block has one argument: [ :anElement :aGtPlotterScaleContext | ... ]" - projectionXLineStyle := aBlock -] - -{ #category : #'api - projections' } -GtPlotterLineChart >> projectionXValues: aBlock [ - "Return projection values. - Block has one argument: [ :aData | ... ]" - projectionXValues := aBlock -] - -{ #category : #accessing } -GtPlotterLineChart >> scaleX [ - ^ scaleX -] - -{ #category : #accessing } -GtPlotterLineChart >> scaleX: anObject [ - scaleX := anObject -] - -{ #category : #accessing } -GtPlotterLineChart >> scaleY [ - ^ scaleY -] - -{ #category : #accessing } -GtPlotterLineChart >> scaleY: anObject [ - scaleY := anObject -] - -{ #category : #'api - axes' } -GtPlotterLineChart >> ticksX: aNumber [ - "Define number of axis ticks" - ticksX := aNumber -] - -{ #category : #'api - axes' } -GtPlotterLineChart >> ticksY: aNumber [ - "Define number of axis ticks" - ticksY := aNumber -] - -{ #category : #'api - axes' } -GtPlotterLineChart >> titleX: aString [ - titleX := aString -] - -{ #category : #'api - axes' } -GtPlotterLineChart >> titleY: aString [ - titleY := aString -] - -{ #category : #accessing } -GtPlotterLineChart >> valueX [ - ^ valueX -] - -{ #category : #accessing } -GtPlotterLineChart >> valueX: anObject [ - valueX := anObject -] - -{ #category : #accessing } -GtPlotterLineChart >> valueY [ - ^ valueY -] - -{ #category : #accessing } -GtPlotterLineChart >> valueY: anObject [ - valueY := anObject -] - { #category : #'api - data' } GtPlotterLineChart >> with: aGtPlotterDataGroup [ data := aGtPlotterDataGroup diff --git a/src/GToolkit-Plotter/GtPlotterScatterChart.class.st b/src/GToolkit-Plotter/GtPlotterScatterChart.class.st index 2a3501456..040bddc1a 100644 --- a/src/GToolkit-Plotter/GtPlotterScatterChart.class.st +++ b/src/GToolkit-Plotter/GtPlotterScatterChart.class.st @@ -1,23 +1,7 @@ Class { #name : #GtPlotterScatterChart, - #superclass : #GtPlotterBuilder, + #superclass : #GtPlotterXYChart, #instVars : [ - 'data', - 'scaleX', - 'scaleY', - 'ticksX', - 'ticksY', - 'titleX', - 'titleY', - 'valueX', - 'valueY', - 'labelFormatX', - 'labelFormatY', - 'isAlreadyInitialized', - 'minX', - 'maxX', - 'minY', - 'maxY', 'dotElementStencil', 'valuesX', 'valuesY', @@ -36,7 +20,7 @@ Class { { #category : #accessing } GtPlotterScatterChart >> axisXStencil [ - ^ axisXStencil + ^ axisXStencil scatterChart: self ] { #category : #accessing } @@ -46,7 +30,7 @@ GtPlotterScatterChart >> axisXStencil: aGtPlotterHorizontalTicksAndLabelStencil { #category : #accessing } GtPlotterScatterChart >> axisYStencil [ - ^ axisYStencil + ^ axisYStencil scatterChart: self ] { #category : #accessing } @@ -96,11 +80,6 @@ GtPlotterScatterChart >> create [ ^ aContainer ] -{ #category : #accessing } -GtPlotterScatterChart >> data [ - ^ data -] - { #category : #accessing } GtPlotterScatterChart >> dotElement [ ^ dotElementStencil @@ -268,66 +247,6 @@ GtPlotterScatterChart >> initializeScales [ ] -{ #category : #accessing } -GtPlotterScatterChart >> labelFormatX [ - ^ labelFormatX -] - -{ #category : #accessing } -GtPlotterScatterChart >> labelFormatX: anObject [ - labelFormatX := anObject -] - -{ #category : #accessing } -GtPlotterScatterChart >> labelFormatY [ - ^ labelFormatY -] - -{ #category : #accessing } -GtPlotterScatterChart >> labelFormatY: anObject [ - labelFormatY := anObject -] - -{ #category : #accessing } -GtPlotterScatterChart >> maxX [ - ^ maxX -] - -{ #category : #accessing } -GtPlotterScatterChart >> maxX: anObject [ - maxX := anObject -] - -{ #category : #accessing } -GtPlotterScatterChart >> maxY [ - ^ maxY -] - -{ #category : #accessing } -GtPlotterScatterChart >> maxY: anObject [ - maxY := anObject -] - -{ #category : #accessing } -GtPlotterScatterChart >> minX [ - ^ minX -] - -{ #category : #accessing } -GtPlotterScatterChart >> minX: anObject [ - minX := anObject -] - -{ #category : #accessing } -GtPlotterScatterChart >> minY [ - ^ minY -] - -{ #category : #accessing } -GtPlotterScatterChart >> minY: anObject [ - minY := anObject -] - { #category : #'api - instantiation' } GtPlotterScatterChart >> newAxisXElement [ ^ axisXStencil @@ -425,93 +344,11 @@ GtPlotterScatterChart >> pointEventHandler: aGtPlotterScatterFocusPointHandler [ pointEventHandler scatterChart: self. ] -{ #category : #accessing } -GtPlotterScatterChart >> scaleX [ - ^ scaleX -] - -{ #category : #accessing } -GtPlotterScatterChart >> scaleX: anObject [ - scaleX := anObject -] - -{ #category : #accessing } -GtPlotterScatterChart >> scaleY [ - ^ scaleY -] - -{ #category : #accessing } -GtPlotterScatterChart >> scaleY: anObject [ - scaleY := anObject -] - { #category : #'api - instantiation' } GtPlotterScatterChart >> scaledPoint: eachValue [ ^ (scaleX map: (valueX value: eachValue)) @ (1 - (scaleY map: (valueY value: eachValue))) ] -{ #category : #accessing } -GtPlotterScatterChart >> ticksX [ - ^ ticksX -] - -{ #category : #accessing } -GtPlotterScatterChart >> ticksX: aNumber [ - "Define number of axis ticks" - ticksX := aNumber -] - -{ #category : #accessing } -GtPlotterScatterChart >> ticksY [ - ^ ticksY -] - -{ #category : #accessing } -GtPlotterScatterChart >> ticksY: aNumber [ - "Define number of axis ticks" - ticksY := aNumber -] - -{ #category : #accessing } -GtPlotterScatterChart >> titleX [ - ^ titleX -] - -{ #category : #accessing } -GtPlotterScatterChart >> titleX: aString [ - titleX := aString -] - -{ #category : #accessing } -GtPlotterScatterChart >> titleY [ - ^ titleY -] - -{ #category : #accessing } -GtPlotterScatterChart >> titleY: aString [ - titleY := aString -] - -{ #category : #accessing } -GtPlotterScatterChart >> valueX [ - ^ valueX -] - -{ #category : #accessing } -GtPlotterScatterChart >> valueX: anObject [ - valueX := anObject -] - -{ #category : #accessing } -GtPlotterScatterChart >> valueY [ - ^ valueY -] - -{ #category : #accessing } -GtPlotterScatterChart >> valueY: anObject [ - valueY := anObject -] - { #category : #accessing } GtPlotterScatterChart >> valuesX [ ^ valuesX @@ -557,8 +394,10 @@ GtPlotterScatterChart >> withProjectionAxis [ { #category : #accessing } GtPlotterScatterChart >> withTicksAndLabelsAxis [ - self axisYStencil: GtPlotterVerticalTicksAndLabelsAxisStencil new. - self axisXStencil: GtPlotterHorizontalTicksAndLabelsAxisStencil new. + self + axisYStencil: (GtPlotterVerticalTicksAndLabelsAxisStencil new scatterChart: self). + self + axisXStencil: (GtPlotterHorizontalTicksAndLabelsAxisStencil new scatterChart: self) ] { #category : #accessing } diff --git a/src/GToolkit-Plotter/GtPlotterXYChart.class.st b/src/GToolkit-Plotter/GtPlotterXYChart.class.st new file mode 100644 index 000000000..dbb74affb --- /dev/null +++ b/src/GToolkit-Plotter/GtPlotterXYChart.class.st @@ -0,0 +1,263 @@ +Class { + #name : #GtPlotterXYChart, + #superclass : #GtPlotterBuilder, + #instVars : [ + 'data', + 'valueX', + 'scaleX', + 'scaleY', + 'valueY', + 'ticksX', + 'ticksY', + 'titleX', + 'titleY', + 'minX', + 'maxX', + 'minY', + 'maxY', + 'labelFormatX', + 'labelFormatY', + 'projectionXLabelElement', + 'projectionXValues', + 'projectionXLabelStyle', + 'projectionXLineStyle', + 'isAlreadyInitialized' + ], + #category : #'GToolkit-Plotter-Builder - XY Chart' +} + +{ #category : #'api - instantiation' } +GtPlotterXYChart >> contentStencil [ + ^ [ | anElement | + self initializeScales. + anElement := self newContentElement. + self styleChartElement: anElement. + anElement ] asStencil +] + +{ #category : #'api - data' } +GtPlotterXYChart >> data [ + ^ data +] + +{ #category : #initalization } +GtPlotterXYChart >> initializeScales [ + self subclassResponsibility +] + +{ #category : #accessing } +GtPlotterXYChart >> labelFormatX [ + ^ labelFormatX +] + +{ #category : #'api - axes' } +GtPlotterXYChart >> labelFormatX: aBlock [ + "Create a {{gtClass:BlText}}. + Block has one argument [ :aValue | ... ]" + labelFormatX := aBlock +] + +{ #category : #accessing } +GtPlotterXYChart >> labelFormatY [ + ^ labelFormatY +] + +{ #category : #'api - axes' } +GtPlotterXYChart >> labelFormatY: aBlock [ + "Create a {{gtClass:BlText}}. + Block has one argument [ :aValue | ... ]" + labelFormatY := aBlock +] + +{ #category : #accessing } +GtPlotterXYChart >> maxX [ + ^ maxX +] + +{ #category : #accessing } +GtPlotterXYChart >> maxX: anObject [ + maxX := anObject +] + +{ #category : #accessing } +GtPlotterXYChart >> maxY [ + ^ maxY +] + +{ #category : #accessing } +GtPlotterXYChart >> maxY: anObject [ + maxY := anObject +] + +{ #category : #accessing } +GtPlotterXYChart >> minX [ + ^ minX +] + +{ #category : #accessing } +GtPlotterXYChart >> minX: anObject [ + minX := anObject +] + +{ #category : #accessing } +GtPlotterXYChart >> minY [ + ^ minY +] + +{ #category : #accessing } +GtPlotterXYChart >> minY: anObject [ + minY := anObject +] + +{ #category : #'instance creation' } +GtPlotterXYChart >> newContentElement [ + self subclassResponsibility +] + +{ #category : #'instance creation' } +GtPlotterXYChart >> newProjectionXLabelsElement [ + | anElement | + anElement := GtPlotterHorizontalValueProjectionsElement new + scale: self scaleX; + scaleData: valueX; + clipChildren: false; + constraintsDo: [ :c | c vertical fitContent ]; + hideOverlapping; + values: (projectionXValues cull: self data). + + projectionXLabelElement ifNotNil: [ + anElement valueElement: projectionXLabelElement ]. + + projectionXLabelStyle ifNotNil: [ + anElement valueStyle: projectionXLabelStyle ]. + + ^ anElement +] + +{ #category : #'instance creation' } +GtPlotterXYChart >> newProjectionXLinesElement [ + ^ GtPlotterHorizontalValueProjectionsElement new + scale: self scaleX; + scaleData: valueX; + valueStyle: projectionXLineStyle; + values: (projectionXValues cull: self data) +] + +{ #category : #'api - instantiation' } +GtPlotterXYChart >> projectionLabelXStencil [ + (self data size isZero or: [ projectionXValues isNil ]) + ifTrue: [ ^ nil ]. + self initializeScales. + ^ BrValuableStencil new + valuable: [ self newProjectionXLabelsElement ] +] + +{ #category : #'api - projections' } +GtPlotterXYChart >> projectionXLabelElement: aBlock [ + "Return projection label element. + Block has one argument: [ :aGtPlotterSingleScaleContext | ... ]" + projectionXLabelElement := aBlock +] + +{ #category : #'api - projections' } +GtPlotterXYChart >> projectionXLabelStyle: aBlock [ + "Style a label element. + Block has one argument: [ :anElement | ... ]" + projectionXLabelStyle := aBlock +] + +{ #category : #'api - projections' } +GtPlotterXYChart >> projectionXLineStyle: aBlock [ + "Style a label element. + Block has one argument: [ :anElement :aGtPlotterScaleContext | ... ]" + projectionXLineStyle := aBlock +] + +{ #category : #'api - projections' } +GtPlotterXYChart >> projectionXValues: aBlock [ + "Return projection values. + Block has one argument: [ :aData | ... ]" + projectionXValues := aBlock +] + +{ #category : #accessing } +GtPlotterXYChart >> scaleX [ + ^ scaleX +] + +{ #category : #accessing } +GtPlotterXYChart >> scaleX: anObject [ + scaleX := anObject +] + +{ #category : #accessing } +GtPlotterXYChart >> scaleY [ + ^ scaleY +] + +{ #category : #accessing } +GtPlotterXYChart >> scaleY: anObject [ + scaleY := anObject +] + +{ #category : #accessing } +GtPlotterXYChart >> ticksX [ + ^ ticksX +] + +{ #category : #accessing } +GtPlotterXYChart >> ticksX: aNumber [ + "Define number of axis ticks" + ticksX := aNumber +] + +{ #category : #accessing } +GtPlotterXYChart >> ticksY [ + ^ ticksY +] + +{ #category : #accessing } +GtPlotterXYChart >> ticksY: aNumber [ + "Define number of axis ticks" + ticksY := aNumber +] + +{ #category : #accessing } +GtPlotterXYChart >> titleX [ + ^ titleX +] + +{ #category : #accessing } +GtPlotterXYChart >> titleX: aString [ + titleX := aString +] + +{ #category : #accessing } +GtPlotterXYChart >> titleY [ + ^ titleY +] + +{ #category : #accessing } +GtPlotterXYChart >> titleY: aString [ + titleY := aString +] + +{ #category : #accessing } +GtPlotterXYChart >> valueX [ + ^ valueX +] + +{ #category : #accessing } +GtPlotterXYChart >> valueX: anObject [ + valueX := anObject +] + +{ #category : #accessing } +GtPlotterXYChart >> valueY [ + ^ valueY +] + +{ #category : #accessing } +GtPlotterXYChart >> valueY: anObject [ + valueY := anObject +]