Skip to content

Commit

Permalink
Adds a notation and scale
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeAtHPI committed Oct 17, 2023
1 parent f9dde49 commit fb12106
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 0 deletions.
98 changes: 98 additions & 0 deletions packages/Sandblocks-Watch/SBAxisNotation.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"
A numerical legend for a scale
"
Class {
#name : #SBAxisNotation,
#superclass : #Morph,
#instVars : [
'scale',
'numberTicks'
],
#category : #'Sandblocks-Watch'
}

{ #category : #'initialize-release' }
SBAxisNotation class >> newFromScale: aSBScale ticking: aNumber [

^ self new
scale: aSBScale
numberTicks: aNumber
]

{ #category : #initialization }
SBAxisNotation >> initialize [

super initialize.

numberTicks := 3.
scale := SBScale newLinearScaleWithDomain: (0 to: 100) forRange: (0 to: 100).

self color: Color transparent;
layoutPolicy: ProportionalLayout new;
hResizing: #shrinkWrap;
vResizing: #spaceFill
]

{ #category : #accessing }
SBAxisNotation >> numberTicks [

^ numberTicks
]

{ #category : #accessing }
SBAxisNotation >> numberTicks: aNumber [

numberTicks := aNumber.

self visualize
]

{ #category : #visualization }
SBAxisNotation >> relativeTickHeights [

| section adjustedTicks |
(self numberTicks < 2) ifTrue: [^#()].

"Starting count from 0 here instead of 1"
adjustedTicks := self numberTicks - 1.
section := 1 / adjustedTicks.
^ (0 to: adjustedTicks) collect: [:i | (section * i)]
]

{ #category : #accessing }
SBAxisNotation >> scale [

^ scale
]

{ #category : #accessing }
SBAxisNotation >> scale: aSBScale [

scale := aSBScale.

self visualize
]

{ #category : #accessing }
SBAxisNotation >> scale: aSBScale numberTicks: aNumber [

scale := aSBScale.
numberTicks := aNumber.

self visualize
]

{ #category : #visualization }
SBAxisNotation >> visualize [

self submorphs copy do: #abandon.
self relativeTickHeights withIndexDo: [:aFraction :i | | annotation |
annotation := SBOwnTextMorph new
contents: ((self scale scaledValueOfRelative: aFraction) asString);
layoutFrame: (LayoutFrame new topFraction: aFraction).
"Fraction 1 will result in the text being just underneath ourself, so substract height as offset"
(aFraction = 1) ifTrue: [annotation layoutFrame topOffset: (-1*(annotation minExtent y))].
self addMorph: annotation.]


]
98 changes: 98 additions & 0 deletions packages/Sandblocks-Watch/SBScale.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"
A scale inspired by their usage in d3. Define a domain from which a set of numbers gets projected to your defined range. Both are Interval objects. As operations like min or array accesses don't work on reversed ordered Intervals (eg meaning they're sorted descendingly), only ascending intervals work.
A scale behavior is a block accepting a domain and a value to scale. The output is a percentage [0;1] representing its relative position in your domain. It can scale the value linear, logarithmic, exponentially etc. For an example, view class > linearScale.
"
Class {
#name : #SBScale,
#superclass : #Object,
#instVars : [
'range',
'domain',
'scaleBehavior'
],
#category : #'Sandblocks-Watch'
}

{ #category : #scaleBehaviors }
SBScale class >> linearScaleBehavior [

^ [:domain :value | ((value - domain min) / {domain extent. 1} max) abs]
]

{ #category : #'initialize-release' }
SBScale class >> newLinearScaleWithDomain: aDomainInterval forRange: aRangeInterval [

^ self new
domain: (
{aDomainInterval start. aDomainInterval stop} min
to: {aDomainInterval start. aDomainInterval stop} max);
range: (
{aRangeInterval start. aRangeInterval stop} min
to: {aRangeInterval start. aRangeInterval stop} max);
scaleBehavior: self linearScaleBehavior;
yourself
]

{ #category : #'initialize-release' }
SBScale class >> newWithDomain: aDomainInterval forRange: aRangeInterval scalingLike: aScaleBehavior [

^ self new
domain: aDomainInterval;
range: aRangeInterval;
scaleBehavior: aScaleBehavior;
yourself.
]

{ #category : #accessing }
SBScale >> domain [

^ domain
]

{ #category : #accessing }
SBScale >> domain: anInterval [

domain := anInterval
]

{ #category : #accessing }
SBScale >> range [

^ range
]

{ #category : #accessing }
SBScale >> range: anInterval [

range := anInterval
]

{ #category : #accessing }
SBScale >> scaleBehavior [

^ scaleBehavior
]

{ #category : #accessing }
SBScale >> scaleBehavior: aBlock [

scaleBehavior := aBlock
]

{ #category : #calculating }
SBScale >> scaledValueOf: aNumberInDomain [

"In case the given number is the start of the domain, meaning always at 0% of total range,
we might get a ZeroDivide during calculations, so catch the case early by return."
(aNumberInDomain = self domain first) ifTrue: [^ self range first].

^ self range at: (self scaleBehavior value: self domain value: aNumberInDomain) * self range extent +1
]

{ #category : #calculating }
SBScale >> scaledValueOfRelative: aNumberFrom0To1 [

"LERP the given number in domain before proceeding normally"
^ self scaledValueOf: ((1 - aNumberFrom0To1) * self domain min + aNumberFrom0To1 * self domain max)
]

0 comments on commit fb12106

Please sign in to comment.