Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add NumberGlyph visualizer #231

Merged
merged 64 commits into from
Apr 21, 2024
Merged
Changes from 14 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
3bb0387
Color combination of prime factors
LeongHouHeng Nov 5, 2022
8f70e1a
Bug fixed. Description panel added.
LeongHouHeng Nov 10, 2022
5a0329d
Merge branch 'numberscope:main' into jenny_branch
LeongHouHeng Nov 12, 2022
a0ff100
Add show/hide feature for the description panel
LeongHouHeng Nov 13, 2022
93fcf2b
Repeated values bug fixed
LeongHouHeng Nov 13, 2022
90c3268
Bug fixed
LeongHouHeng Nov 13, 2022
6881a3a
Merge branch 'numberscope:main' into jenny_branch
LeongHouHeng Nov 16, 2022
be7fbf9
Fix issues stated in initial review
LeongHouHeng Nov 17, 2022
c915b06
Merge branch 'jenny_branch' of https://github.com/LeongHouHeng/fronts…
LeongHouHeng Nov 17, 2022
572c0b9
Merge branch 'numberscope:main' into jenny_branch
LeongHouHeng Dec 7, 2022
fca24d1
Bugs fixed, features added.
LeongHouHeng Dec 7, 2022
01eab88
Merge branch 'jenny_branch' of https://github.com/LeongHouHeng/fronts…
LeongHouHeng Dec 7, 2022
4a10500
Bug fixed, and features added
LeongHouHeng Dec 7, 2022
e64d984
refactor to use p5.Vector and getFactors() and rename
katestange Jan 6, 2023
a600112
allow user input of a function for growth, and showing growth rings
katestange Jan 6, 2023
96f2e0d
make the circles resize to fit the number of terms requested
katestange Jan 7, 2023
710633c
fix first circle bug
katestange Jan 7, 2023
3ca2f16
remove errant console.log and take absolute values of terms
katestange Jan 7, 2023
b3e5d35
remove errant console.log
katestange Jan 7, 2023
9134255
simplify computation of brightness value and allow for negative value…
katestange Jan 7, 2023
698b78c
fix primelist bug
katestange Jan 7, 2023
aa16257
tweaks
katestange Jan 7, 2023
ef8d516
documentation
katestange Jan 7, 2023
68eee77
fix upper right bug
katestange Jan 7, 2023
14c147a
fix offset bug and include semiprimes example
katestange Jan 7, 2023
effb772
Merge branch 'main' into seq_color_visualizer
gwhitney Jan 10, 2023
acb7021
Color combination of prime factors
LeongHouHeng Nov 5, 2022
92d51e7
Bug fixed. Description panel added.
LeongHouHeng Nov 10, 2022
bbe8996
Add show/hide feature for the description panel
LeongHouHeng Nov 13, 2022
aab8e60
Repeated values bug fixed
LeongHouHeng Nov 13, 2022
32b1b75
Bug fixed
LeongHouHeng Nov 13, 2022
fefb929
Fix issues stated in initial review
LeongHouHeng Nov 17, 2022
b4be1d3
Bugs fixed, features added.
LeongHouHeng Dec 7, 2022
1d306e0
Bug fixed, and features added
LeongHouHeng Dec 7, 2022
1ee6180
refactor to use p5.Vector and getFactors() and rename
katestange Jan 6, 2023
073143d
allow user input of a function for growth, and showing growth rings
katestange Jan 6, 2023
65636ce
make the circles resize to fit the number of terms requested
katestange Jan 7, 2023
4820e50
fix first circle bug
katestange Jan 7, 2023
caed9f2
remove errant console.log and take absolute values of terms
katestange Jan 7, 2023
aeab2b2
remove errant console.log
katestange Jan 7, 2023
afb9764
simplify computation of brightness value and allow for negative value…
katestange Jan 7, 2023
21b697a
fix primelist bug
katestange Jan 7, 2023
4d0a0c9
tweaks
katestange Jan 7, 2023
306244b
documentation
katestange Jan 7, 2023
17707af
fix upper right bug
katestange Jan 7, 2023
0fe433d
fix offset bug and include semiprimes example
katestange Jan 7, 2023
727f0de
rebase and rename to NumberGlyph
katestange Jan 18, 2023
e2ea0e5
Merge branch 'seq_color_visualizer' of https://github.com/numberscope…
katestange Jan 18, 2023
9f581c5
completed removal of SeqColor.ts
katestange Jan 18, 2023
ae8438b
update desc and one more spot to change name
katestange Jan 18, 2023
27d59aa
create customize parameter that hides glyph function
katestange Jan 18, 2023
5ecc7b4
create customize parameter that hides glyph function
katestange Jan 18, 2023
8f67629
try to handle bigint errors
katestange Jan 18, 2023
6db217e
make unfactorables and 0,1,-1 grey
katestange Jan 18, 2023
8dc3ef8
documentation and examples
katestange Jan 18, 2023
64f5a00
Merge branch 'main' into seq_color_visualizer
katestange Apr 21, 2024
db780bc
Initial changes to address pr #277
katestange Apr 21, 2024
802ba30
works with #277 refactor
katestange Apr 21, 2024
ed3114c
take out keyboard interaction
katestange Apr 21, 2024
7128195
cleanup
katestange Apr 21, 2024
8b57ef8
delete inhabit
katestange Apr 21, 2024
80514bd
fix: Fit glyphs to canvas; also avoid TypeScript hack
gwhitney Apr 21, 2024
4142b3c
doc: Correct the description of the number of disc positions
gwhitney Apr 21, 2024
5994911
fix: Use absolute value of brightness function, and document it
gwhitney Apr 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
390 changes: 390 additions & 0 deletions src/visualizers/SeqColor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,390 @@
import type p5 from 'p5'
import {VisualizerDefault} from './VisualizerDefault'
import type {VisualizerInterface} from '@/visualizers/VisualizerInterface'
import {VisualizerExportModule} from '@/visualizers/VisualizerInterface'
import type {SequenceInterface} from '../sequences/SequenceInterface'

/** md

# Primes and Sizes Visualizer

In this visualizer, the brightness of each circle represents how fast $1/a^n$,
where a is a number in the sequence,
approaches zero as n increases.
Each prime number is assigned a color,
and the color of each circle represents
the color combination of its prime factors. \s

Press the "Left arrow key" to show/hide the color of each prime factor.\s
Press the "Right arrow key" to show/hide labels.\s
Press "[" to turn up brightness.\s
Press "]" to turn down brightness. \s
**/

const colorMap = new Map()

// Show description box by pressing left arrow key
class SeqColor extends VisualizerDefault implements VisualizerInterface {
name = 'Primes and Sizes'
n = 40

params = {
n: {
value: this.n,
forceType: 'integer',
displayName: 'number of terms',
required: true,
},
}

private currentIndex = 0
private position = this.sketch.createVector(0, 0)
private boxSize = this.sketch.createVector(0, 0)
private canvasSize = this.sketch.createVector(0, 0)
private subG = this.sketch.createGraphics(0, 0)
private subL = this.sketch.createGraphics(0, 0)
private boxIsShow = false
private primeNum: bigint[] = []
private countPrime = 0
private firstDraw = true
private showLabel = false
private brightAdjust = 100

initialize(sketch: p5, seq: SequenceInterface) {
super.initialize(sketch, seq)
this.sketch = sketch
this.seq = seq
this.currentIndex = seq.first
this.position = this.sketch.createVector(0, 0)
this.ready = true
this.boxSize = this.sketch.createVector(800, 90)
this.subG = this.sketch.createGraphics(this.boxSize.x, this.boxSize.y)
this.canvasSize = this.sketch.createVector(800, 800)
this.subL = this.sketch.createGraphics(
this.canvasSize.x,
this.canvasSize.y
)
}

setup() {
this.sketch.background('black')
this.sketch.colorMode(this.sketch.HSB, 360, 100, 100)
this.sketch.frameRate(30)

this.firstDraw = true

// Set position of the circle
this.position = this.sketch.createVector(
this.canvasSize.x / this.n + 30,
this.canvasSize.x / this.n + 50
)

// Obtain all prime numbers from the sequence
for (let i = this.seq.first; i < this.n; i++) {
const checkCurrentPrime = this.seq.getElement(i)
if (
this.isPrime(i)
&& !this.primeNum.includes(checkCurrentPrime)
) {
this.primeNum.push(checkCurrentPrime)
this.countPrime += 1
}
}
}

draw() {
if (this.firstDraw == true && this.currentIndex < this.n) {
this.drawCircle(this.currentIndex++)

this.changePosition()

// Check if drawing finished
if (this.currentIndex >= this.n) {
this.firstDraw = false
}
this.sketch.noStroke()
} else {
// Monitor keyboard events after finishing drawing
// Bug: multiple clicks detected when only one click happened
this.keyboardEvents()
}
}
/** The following code can be used to fix the keyboardEvents() bug
* once issue #120 is resolved
keyPressed() {
if (this.sketch.keyCode === this.sketch.LEFT_ARROW) {
// Show/hide description box when the left arrow key is pressed
if (this.boxIsShow == true && this.subG != null) {
this.boxIsShow = false
this.undrawBox()
} else {
this.boxIsShow = true
this.drawBox()
}
} else if (this.sketch.keyCode === this.sketch.RIGHT_ARROW) {
// Show/hide label when right arrow key is pressed
if (this.showLabel == false) {
this.showLabel = true
this.drawLabel()
} else {
this.showLabel = false
this.undrawLabel()
}
}
}
**/
keyboardEvents() {
// Show description box when the left arrow key is pressed
if (this.sketch.keyIsDown(this.sketch.LEFT_ARROW)) {
if (this.boxIsShow == true && this.subG != null) {
this.boxIsShow = false
this.undrawBox()
} else {
this.boxIsShow = true
this.drawBox()
}
}

// Show label when right arrow key is pressed
if (
this.sketch.keyIsDown(this.sketch.RIGHT_ARROW)
&& this.firstDraw == false
) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about negative terms? Right now it draws black. I suggest you just take the absolute values of terms, so that the sequences will converge. If you do this, you should also document this behaviour in the documentation above.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in a recent commit (except not yet documented). now it takes the absolute value of any growth function

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't seem to work; if I make a growth function that returns all negative values (like multiplying the default one by -1), I get a completely black canvas. Please could you do the abs-ing claimed here and document this aspect? If you don't get to it before I get back from my morning bike ride, I will do it when I get back, no problem.

if (this.showLabel == false) {
this.showLabel = true
this.drawLabel()
} else {
this.showLabel = false
this.undrawLabel()
}
}

// Increase brightness "[" key is pressed
if (this.sketch.keyIsDown(219) && this.firstDraw == false) {
this.brightnessUp()
}

// Decrease brightness "]" key is pressed
if (this.sketch.keyIsDown(221) && this.firstDraw == false) {
this.brightnessDown()
}
}

brightnessUp() {
this.brightAdjust += 1
this.redrawCircle()
}

brightnessDown() {
this.brightAdjust -= 1
this.redrawCircle()
}

// Draw labels for each circle
drawLabel() {
this.position = this.sketch.createVector(
this.canvasSize.x / this.n + 30,
this.canvasSize.y / this.n + 50
)
this.subL = this.sketch.createGraphics(
this.canvasSize.x,
this.canvasSize.y
)
this.subG.colorMode(this.sketch.HSB)
this.subG.noStroke()
for (let i = this.seq.first; i < this.n; i++) {
this.showCircleLabel(i)
this.changePosition()
}
}

// Remove all labels by drawing circles again
undrawLabel() {
this.redrawCircle()
}

redrawCircle() {
this.position = this.sketch.createVector(
this.canvasSize.x / this.n + 30,
this.canvasSize.x / this.n + 50
)
this.firstDraw = true
this.currentIndex = this.seq.first
this.sketch.redraw(1)
}

drawCircle(ind: number) {
const numberNow = this.seq.getElement(ind)
this.sketch.ellipseMode(this.sketch.RADIUS)
let radius = 50
let bright = 0

for (let x = this.n; x >= 2; x--) {
// Calculate the difference
// between 1/(number)^n and 1/(number)^(n-1)
const diff = Math.abs(
Math.log(
Math.abs(
1 / Math.pow(Number(numberNow), x - 1)
- Math.log(1 / Math.pow(Number(numberNow), x))
)
)
)

// Obtain the color of the circle
const combinedColor = this.primeFactors(
ind,
Number(this.countPrime)
)

this.sketch.colorMode(this.sketch.HSB)
this.sketch.fill(combinedColor, 100, bright)
this.sketch.ellipse(
this.position.x,
this.position.y,
radius,
radius
)

// Change brightness regarding
// the difference between 1/(number)^n and 1/(number)^(n-1)
bright =
(this.changeColor(diff, bright) * this.brightAdjust) / 100

radius -= 1
}
}

showCircleLabel(numberNow: number) {
this.sketch.fill('white')
this.sketch.text(
'1/'.concat(String(numberNow)).concat('^n'),
this.position.x,
this.position.y
)
}

changePosition() {
this.position.add(100, 0)
// if we need to go to next line
if (this.position.x >= this.canvasSize.x) {
this.position.x = this.canvasSize.x / this.n + 30
this.position.add(0, 100)
}
}

drawBox() {
//Create a white background for the description box
this.subG = this.sketch.createGraphics(this.boxSize.x, this.boxSize.y)
this.subG.colorMode(this.sketch.HSB)
this.subG.noStroke()
this.subG.fill(0, 0, 100)
this.subG.rect(0, 0, this.boxSize.x, this.boxSize.y)
this.subG.fill('black')
let tmpX = 0
let tmpY = 0

//Show the color of every prime number
for (let i = 0; i < this.primeNum.length; i++) {
this.subG.fill('black')
this.subG.text(String(this.primeNum[i]), 10 + tmpX, 15 + tmpY)
this.subG.fill(colorMap.get(this.primeNum[i]), 100, 100)

this.subG.ellipse(35 + tmpX, 15 + tmpY, 10, 10)
tmpX += 50
if (tmpX >= this.canvasSize.x) {
tmpX = 0
tmpY += 30
}
}
this.sketch.image(this.subG, 0, 700)
}

undrawBox() {
if (this.subG) {
//Draw a new black background to cover the description box
this.subG.fill('black')
this.subG.rect(0, 0, this.boxSize.x, this.boxSize.y)
this.sketch.image(this.subG, 0, 700)
}
}

//change the brightness of the circle
changeColor(difference: number, now: number) {
if ((now + difference) % 100 < now) {
return 100
}
return (now + difference) % 100
}

isPrime(ind: number): boolean {
const factors = this.seq.getFactors(ind)
if (
factors === null // if we can't factor, it isn't prime
|| factors.length === 0 // 1 is not prime
|| factors[0][0] === 0n // 0 is not prime
|| (factors.length === 1 && factors[0][0] === -1n) // -1 not prime
) {
return false
}
if (
(factors.length === 1 && factors[0][1] === 1n) // prime
|| (factors.length === 2
&& factors[0][0] === -1n
&& factors[1][1] == 1n) // negative of prime
) {
return true
} else {
return false
}
}

//return a number which represents the color
primeFactors(ind: number, totalPrime: number) {
const factors = this.seq.getFactors(ind)
if (factors === null) {
return -1
} // factoring failed

//assign color to each prime number
const colorNum = 360 / Number(totalPrime)

colorMap.set(1, 0)
let tmp = 0

for (let i = 0; i < this.primeNum.length; i++) {
if (colorMap.has(this.primeNum[i]) == false) {
tmp += colorNum
colorMap.set(this.primeNum[i], tmp)
}
}

//Combine color for each prime factors
let colorAll = -1
for (let i = 0; i < factors.length; i++) {
const thisPrime = factors[i][0]
const thisExp = factors[i][1]
for (let j = 0; j < this.primeNum.length; j++) {
if (thisPrime == this.primeNum[j]) {
for (let k = 0; k < thisExp; k++) {
if (colorAll == -1) {
colorAll = colorMap.get(thisPrime)
} else {
colorAll =
(colorAll + colorMap.get(thisPrime)) / 2
}
}
}
}
}

return colorAll
}
}

export const exportModule = new VisualizerExportModule(
'Sequence Color',
SeqColor,
''
)
// Bug: First circle for n+2, n+3, etc. is not shown properly