diff --git a/.all-contributorsrc b/.all-contributorsrc index d60fb8c632..bbea31f828 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2,7 +2,7 @@ "projectName": "processing4", "projectOwner": "processing", "files": [ - "README.md" + "CONTRIBUTORS.md" ], "imageSize": 120, "contributorsPerLine": 6, diff --git a/BUILD.md b/BUILD.md index a7176776a2..1216f2e952 100644 --- a/BUILD.md +++ b/BUILD.md @@ -163,3 +163,16 @@ You may see this warning in IntelliJ: > `Duplicate content roots detected: '.../processing4/java/src'` This happens because multiple modules reference the same source folder. It’s safe to ignore. + + +### Build Failed + +If the build fails with `Permission denied` or `Could not copy file` errors, try cleaning the project. + +Run: + +```bash +./gradlew clean +``` + +Then, rebuild the project. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000000..38efdebfc8 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,258 @@ +_Note: due to GitHub's limitations, this repository's [Contributors](https://github.com/processing/processing4/graphs/contributors) page only shows accurate contribution data starting from late 2024. Contributor graphs from before November 13th 2024 can be found on [this page](https://github.com/benfry/processing4/graphs/contributors). The [git commit history](https://github.com/processing/processing4/commits/main/) provides a full record of the project's contributions. To see all commits by a contributor, click on the [πŸ’»](https://github.com/processing/processing4/commits?author=benfry) emoji below their name._ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Ben Fry
Ben Fry

πŸ’» πŸ€” πŸš‡ πŸ§‘β€πŸ« 🚧 πŸ–‹ πŸ“’
Casey Reas
Casey Reas

πŸ’» πŸ€” πŸš‡ πŸ§‘β€πŸ« πŸ–‹ πŸ“’ βœ…
codeanticode
codeanticode

πŸ’»
Manindra Moharana
Manindra Moharana

πŸ’»
Jakub Valtar
Jakub Valtar

πŸ’»
A Samuel Pottinger
A Samuel Pottinger

πŸ’»
Gottfried Haider
Gottfried Haider

πŸ’»
Akarshit Wal
Akarshit Wal

πŸ’»
Peter Kalauskas
Peter Kalauskas

πŸ’»
Daniel Shiffman
Daniel Shiffman

πŸ’»
Joel Moniz
Joel Moniz

πŸ’»
Lonnen
Lonnen

πŸ’»
Florian Jenett
Florian Jenett

πŸ’»
Scott Murray
Scott Murray

πŸ’»
Federico Bond
Federico Bond

πŸ’»
pvrs12
pvrs12

πŸ’»
George Bateman
George Bateman

πŸ’»
Sean McKenna
Sean McKenna

πŸ’»
kfeuz
kfeuz

πŸ’»
David Wicks
David Wicks

πŸ’»
Wilm Thoben
Wilm Thoben

πŸ’»
Ana
Ana

πŸ’»
Amnon Owed
Amnon Owed

πŸ’»
Gal Sasson
Gal Sasson

πŸ’»
scollovati
scollovati

πŸ’»
Yong Joseph Bakos
Yong Joseph Bakos

πŸ’»
Kenichi Ito
Kenichi Ito

πŸ’»
Efratror
Efratror

πŸ’»
Alexis Engelke
Alexis Engelke

πŸ’»
tyfkda
tyfkda

πŸ’»
Simon Greenwold
Simon Greenwold

πŸ’»
Rune Skjoldborg Madsen
Rune Skjoldborg Madsen

πŸ’»
Leslie Watkins
Leslie Watkins

πŸ’»
Rostyslav Zatserkovnyi
Rostyslav Zatserkovnyi

πŸ’»
Dan
Dan

πŸ’»
Daniel Howe
Daniel Howe

πŸ’»
Josh Giesbrecht
Josh Giesbrecht

πŸ’» πŸ›
liquidex
liquidex

πŸ’»
bgc
bgc

πŸ’»
Mohammad Umair
Mohammad Umair

πŸ’»
T Michail
T Michail

πŸ’»
ohommos
ohommos

πŸ’»
Jonathan Feinberg
Jonathan Feinberg

πŸ’»
David Fokkema
David Fokkema

πŸ’»
liquid
liquid

πŸ’»
Kisaru Liyanage
Kisaru Liyanage

πŸ’»
BouB
BouB

πŸ’»
atk
atk

πŸ’»
Xerxes RΓ₯nby
Xerxes RΓ₯nby

πŸ’»
Will Rabalais
Will Rabalais

πŸ’»
Utkarsh Tiwari
Utkarsh Tiwari

πŸ’»
Prince-Polka
Prince-Polka

πŸ’»
jamesjgrady
jamesjgrady

πŸ’»
RaphaΓ«l de Courville
RaphaΓ«l de Courville

πŸ’»
Satoshi Okita
Satoshi Okita

πŸ’»
Carlos AndrΓ©s Rocha
Carlos AndrΓ©s Rocha

πŸ’»
Vincent Vijn
Vincent Vijn

πŸ’»
dzaima
dzaima

πŸ’»
mingness
mingness

πŸš‡
Dora Do
Dora Do

πŸš‡
Stef Tervelde
Stef Tervelde

πŸ’»
allcontributors[bot]
allcontributors[bot]

πŸ’»
Dave
Dave

πŸ’»
TN8001
TN8001

πŸ’»
Sigmund Hansen
Sigmund Hansen

πŸ’»
Rodrigo BonifΓ‘cio
Rodrigo BonifΓ‘cio

πŸ’»
Aidan Pieper
Aidan Pieper

πŸ’»
Liam James
Liam James

πŸ’»
james gilles
james gilles

πŸ’»
Elie Zananiri
Elie Zananiri

πŸ’»
Cosimo Cecchi
Cosimo Cecchi

πŸ’»
Liam Middlebrook
Liam Middlebrook

πŸ’»
Martin YrjΓΆlΓ€
Martin YrjΓΆlΓ€

πŸ’»
MichaΕ‚ UrbaΕ„ski
MichaΕ‚ UrbaΕ„ski

πŸ’»
Paco
Paco

πŸ’»
Patrick Ryan
Patrick Ryan

πŸ’»
PaweΕ‚ GoliΕ„ski
PaweΕ‚ GoliΕ„ski

πŸ’»
Rupesh Kumar
Rupesh Kumar

πŸ’»
Suhaib Khan
Suhaib Khan

πŸ’»
Yves BLAKE
Yves BLAKE

πŸ’»
M. Ernestus
M. Ernestus

πŸ’»
Francis Li
Francis Li

πŸ’»
Parag Jain
Parag Jain

πŸ’»
roopa vasudevan
roopa vasudevan

πŸ’»
kiwistrongis
kiwistrongis

πŸ’»
Alessandro Ranellucci
Alessandro Ranellucci

πŸ’»
Alexandre B A Villares
Alexandre B A Villares

πŸ’»
Heracles
Heracles

πŸ’»
Arya Gupta
Arya Gupta

πŸ’»
Damien Quartz
Damien Quartz

πŸ’»
Shubham Rathore
Shubham Rathore

πŸ’»
Grigoriy Titaev
Grigoriy Titaev

πŸ’»
Guilherme Silveira
Guilherme Silveira

πŸ’»
HΓ©ctor LΓ³pez Carral
HΓ©ctor LΓ³pez Carral

πŸ’»
Jeremy Douglass
Jeremy Douglass

πŸ’»
Jett LaRue
Jett LaRue

πŸ’»
Jim
Jim

πŸ’» πŸ›
Joan Perals
Joan Perals

πŸ’»
Josh Holinaty
Josh Holinaty

πŸ’»
Keito Takeda
Keito Takeda

πŸ’»
Victor OsΓ³rio
Victor OsΓ³rio

πŸ’»
Torben
Torben

πŸ’»
Tobias Pristupin
Tobias Pristupin

πŸ’»
Thomas Leplus
Thomas Leplus

πŸ’»
Arnoud van der Leer
Arnoud van der Leer

πŸ’»
Stanislas MarΓ§ais / Knupel
Stanislas MarΓ§ais / Knupel

πŸ’»
Sanchit Kapoor
Sanchit Kapoor

πŸ’»
Miles Fogle
Miles Fogle

πŸ’»
Miguel Valadas
Miguel Valadas

πŸ’»
Maximilien Tirard
Maximilien Tirard

πŸ’»
Matthew Russell
Matthew Russell

πŸ’»
dcuartielles
dcuartielles

πŸ’»
Jayson Haebich
Jayson Haebich

πŸ’»
jordirosa
jordirosa

πŸ’»
Justin Shrake
Justin Shrake

πŸ’»
Kevin
Kevin

πŸ’»
kgtkr
kgtkr

πŸ’»
Mark Luffel
Mark Luffel

πŸ’»
Никита ΠšΠΎΡ€ΠΎΠ»ΡŒ
Никита ΠšΠΎΡ€ΠΎΠ»ΡŒ

πŸ’»
raguenets
raguenets

πŸ’»
robog-two
robog-two

πŸ’»
teddywing
teddywing

πŸ’»
chikuwa
chikuwa

πŸ’»
ΰ² _ΰ² 
ΰ² _ΰ² 

πŸ’»
Abe Pazos
Abe Pazos

πŸ’»
Alex
Alex

πŸ’»
Alexander Hurst
Alexander Hurst

πŸ’»
AnΔ±l
AnΔ±l

πŸ’»
Barış
Barış

πŸ’»
Brian Sapozhnikov
Brian Sapozhnikov

πŸ’»
Carlos Mario Rodriguez Perdomo
Carlos Mario Rodriguez Perdomo

πŸ’»
CyberFlame
CyberFlame

πŸ’»
Dhruv Jawali
Dhruv Jawali

πŸ’»
FlorisVO
FlorisVO

πŸ’»
Frank Leon Rose
Frank Leon Rose

πŸ’»
Greg Borenstein
Greg Borenstein

πŸ’»
Guillermo Perez
Guillermo Perez

πŸ’»
Henning Kiel
Henning Kiel

πŸ’»
J David Eisenberg
J David Eisenberg

πŸ’»
Jordan Ephron
Jordan Ephron

πŸ’»
Jason Sigal
Jason Sigal

πŸ’»
Jordan Orelli
Jordan Orelli

πŸ’»
Kalle
Kalle

πŸ’»
Laureano LΓ³pez
Laureano LΓ³pez

πŸ’»
Lesley Wagner
Lesley Wagner

πŸ’»
Mark Slee
Mark Slee

πŸ’»
MARTIN LEOPOLD GROEDL
MARTIN LEOPOLD GROEDL

πŸ’»
Martin Prout
Martin Prout

πŸ’»
Mathias Herberts
Mathias Herberts

πŸ’»
Diya Solanki
Diya Solanki

πŸš‡
Neil C Smith
Neil C Smith

πŸš‡
kate hollenbach
kate hollenbach

πŸ’» πŸ“¦ πŸ§‘β€πŸ« πŸ›
Rishabdev Tudu
Rishabdev Tudu

πŸ“– πŸ’»
Pau
Pau

πŸ“–
Junology
Junology

πŸ’»
Jaap Meijers
Jaap Meijers

πŸ“–
Xin Xin
Xin Xin

πŸ“‹ πŸ€”
Benjamin Fox
Benjamin Fox

πŸ’»
e1dem
e1dem

πŸ’»
Aditya Chaudhary
Aditya Chaudhary

πŸ’»
Rishab Kumar Jha
Rishab Kumar Jha

πŸ’»
Yehia Rasheed
Yehia Rasheed

πŸ’»
Subhraman Sarkar
Subhraman Sarkar

πŸ’» ️️️️♿️ πŸ“–
SushantBansal-tech
SushantBansal-tech

πŸ€” πŸ’»
Konsl
Konsl

πŸ“–
Mario Guzman
Mario Guzman

πŸ“–
Aranya Dutta
Aranya Dutta

πŸ’»
ovalnine
ovalnine

πŸ’»
Joshua D. Boyd
Joshua D. Boyd

πŸ“–
Vaivaswat Dubey
Vaivaswat Dubey

πŸ’»
jSdCool
jSdCool

πŸ’» πŸ“–
AhmedMaged
AhmedMaged

πŸ’»
Nico Mexis
Nico Mexis

πŸ’»
charlotte 🌸
charlotte 🌸

πŸ‘€
Joackim de Bourqueney
Joackim de Bourqueney

πŸ’»
Tonz
Tonz

πŸ’» πŸ“–
Andrew
Andrew

πŸ’»
Ngoc Doan
Ngoc Doan

πŸ’»
Manoel Ribeiro
Manoel Ribeiro

πŸ“–
Moon
Moon

πŸ’»
Nia Perez
Nia Perez

πŸ’»
SuganthiThomas
SuganthiThomas

πŸ’»
+ + + + + \ No newline at end of file diff --git a/README.md b/README.md index 7abe540901..c229dc16c8 100644 --- a/README.md +++ b/README.md @@ -66,263 +66,6 @@ For licensing information about the Processing website see the [processing-websi Copyright (c) 2015-now The Processing Foundation ## Contributors -The Processing project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification, recognizing all forms of contributions (not just code!). A list of all contributors is included below. You can add yourself to the contributors list [here](https://github.com/processing/processing4-carbon-aug-19/issues/839)! +See [CONTRIBUTORS.md](./CONTRIBUTORS.md) for a list of all contributors to the project. -_Note: due to GitHub's limitations, this repository's [Contributors](https://github.com/processing/processing4/graphs/contributors) page only shows accurate contribution data starting from late 2024. Contributor graphs from before November 13th 2024 can be found on [this page](https://github.com/benfry/processing4/graphs/contributors). The [git commit history](https://github.com/processing/processing4/commits/main/) provides a full record of the project's contributions. To see all commits by a contributor, click on the [πŸ’»](https://github.com/processing/processing4/commits?author=benfry) emoji below their name._ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Ben Fry
Ben Fry

πŸ’» πŸ€” πŸš‡ πŸ§‘β€πŸ« 🚧 πŸ–‹ πŸ“’
Casey Reas
Casey Reas

πŸ’» πŸ€” πŸš‡ πŸ§‘β€πŸ« πŸ–‹ πŸ“’ βœ…
codeanticode
codeanticode

πŸ’»
Manindra Moharana
Manindra Moharana

πŸ’»
Jakub Valtar
Jakub Valtar

πŸ’»
A Samuel Pottinger
A Samuel Pottinger

πŸ’»
Gottfried Haider
Gottfried Haider

πŸ’»
Akarshit Wal
Akarshit Wal

πŸ’»
Peter Kalauskas
Peter Kalauskas

πŸ’»
Daniel Shiffman
Daniel Shiffman

πŸ’»
Joel Moniz
Joel Moniz

πŸ’»
Lonnen
Lonnen

πŸ’»
Florian Jenett
Florian Jenett

πŸ’»
Scott Murray
Scott Murray

πŸ’»
Federico Bond
Federico Bond

πŸ’»
pvrs12
pvrs12

πŸ’»
George Bateman
George Bateman

πŸ’»
Sean McKenna
Sean McKenna

πŸ’»
kfeuz
kfeuz

πŸ’»
David Wicks
David Wicks

πŸ’»
Wilm Thoben
Wilm Thoben

πŸ’»
Ana
Ana

πŸ’»
Amnon Owed
Amnon Owed

πŸ’»
Gal Sasson
Gal Sasson

πŸ’»
scollovati
scollovati

πŸ’»
Yong Joseph Bakos
Yong Joseph Bakos

πŸ’»
Kenichi Ito
Kenichi Ito

πŸ’»
Efratror
Efratror

πŸ’»
Alexis Engelke
Alexis Engelke

πŸ’»
tyfkda
tyfkda

πŸ’»
Simon Greenwold
Simon Greenwold

πŸ’»
Rune Skjoldborg Madsen
Rune Skjoldborg Madsen

πŸ’»
Leslie Watkins
Leslie Watkins

πŸ’»
Rostyslav Zatserkovnyi
Rostyslav Zatserkovnyi

πŸ’»
Dan
Dan

πŸ’»
Daniel Howe
Daniel Howe

πŸ’»
Josh Giesbrecht
Josh Giesbrecht

πŸ’» πŸ›
liquidex
liquidex

πŸ’»
bgc
bgc

πŸ’»
Mohammad Umair
Mohammad Umair

πŸ’»
T Michail
T Michail

πŸ’»
ohommos
ohommos

πŸ’»
Jonathan Feinberg
Jonathan Feinberg

πŸ’»
David Fokkema
David Fokkema

πŸ’»
liquid
liquid

πŸ’»
Kisaru Liyanage
Kisaru Liyanage

πŸ’»
BouB
BouB

πŸ’»
atk
atk

πŸ’»
Xerxes RΓ₯nby
Xerxes RΓ₯nby

πŸ’»
Will Rabalais
Will Rabalais

πŸ’»
Utkarsh Tiwari
Utkarsh Tiwari

πŸ’»
Prince-Polka
Prince-Polka

πŸ’»
jamesjgrady
jamesjgrady

πŸ’»
RaphaΓ«l de Courville
RaphaΓ«l de Courville

πŸ’»
Satoshi Okita
Satoshi Okita

πŸ’»
Carlos AndrΓ©s Rocha
Carlos AndrΓ©s Rocha

πŸ’»
Vincent Vijn
Vincent Vijn

πŸ’»
dzaima
dzaima

πŸ’»
mingness
mingness

πŸš‡
Dora Do
Dora Do

πŸš‡
Stef Tervelde
Stef Tervelde

πŸ’»
allcontributors[bot]
allcontributors[bot]

πŸ’»
Dave
Dave

πŸ’»
TN8001
TN8001

πŸ’»
Sigmund Hansen
Sigmund Hansen

πŸ’»
Rodrigo BonifΓ‘cio
Rodrigo BonifΓ‘cio

πŸ’»
Aidan Pieper
Aidan Pieper

πŸ’»
Liam James
Liam James

πŸ’»
james gilles
james gilles

πŸ’»
Elie Zananiri
Elie Zananiri

πŸ’»
Cosimo Cecchi
Cosimo Cecchi

πŸ’»
Liam Middlebrook
Liam Middlebrook

πŸ’»
Martin YrjΓΆlΓ€
Martin YrjΓΆlΓ€

πŸ’»
MichaΕ‚ UrbaΕ„ski
MichaΕ‚ UrbaΕ„ski

πŸ’»
Paco
Paco

πŸ’»
Patrick Ryan
Patrick Ryan

πŸ’»
PaweΕ‚ GoliΕ„ski
PaweΕ‚ GoliΕ„ski

πŸ’»
Rupesh Kumar
Rupesh Kumar

πŸ’»
Suhaib Khan
Suhaib Khan

πŸ’»
Yves BLAKE
Yves BLAKE

πŸ’»
M. Ernestus
M. Ernestus

πŸ’»
Francis Li
Francis Li

πŸ’»
Parag Jain
Parag Jain

πŸ’»
roopa vasudevan
roopa vasudevan

πŸ’»
kiwistrongis
kiwistrongis

πŸ’»
Alessandro Ranellucci
Alessandro Ranellucci

πŸ’»
Alexandre B A Villares
Alexandre B A Villares

πŸ’»
Heracles
Heracles

πŸ’»
Arya Gupta
Arya Gupta

πŸ’»
Damien Quartz
Damien Quartz

πŸ’»
Shubham Rathore
Shubham Rathore

πŸ’»
Grigoriy Titaev
Grigoriy Titaev

πŸ’»
Guilherme Silveira
Guilherme Silveira

πŸ’»
HΓ©ctor LΓ³pez Carral
HΓ©ctor LΓ³pez Carral

πŸ’»
Jeremy Douglass
Jeremy Douglass

πŸ’»
Jett LaRue
Jett LaRue

πŸ’»
Jim
Jim

πŸ’» πŸ›
Joan Perals
Joan Perals

πŸ’»
Josh Holinaty
Josh Holinaty

πŸ’»
Keito Takeda
Keito Takeda

πŸ’»
Victor OsΓ³rio
Victor OsΓ³rio

πŸ’»
Torben
Torben

πŸ’»
Tobias Pristupin
Tobias Pristupin

πŸ’»
Thomas Leplus
Thomas Leplus

πŸ’»
Arnoud van der Leer
Arnoud van der Leer

πŸ’»
Stanislas MarΓ§ais / Knupel
Stanislas MarΓ§ais / Knupel

πŸ’»
Sanchit Kapoor
Sanchit Kapoor

πŸ’»
Miles Fogle
Miles Fogle

πŸ’»
Miguel Valadas
Miguel Valadas

πŸ’»
Maximilien Tirard
Maximilien Tirard

πŸ’»
Matthew Russell
Matthew Russell

πŸ’»
dcuartielles
dcuartielles

πŸ’»
Jayson Haebich
Jayson Haebich

πŸ’»
jordirosa
jordirosa

πŸ’»
Justin Shrake
Justin Shrake

πŸ’»
Kevin
Kevin

πŸ’»
kgtkr
kgtkr

πŸ’»
Mark Luffel
Mark Luffel

πŸ’»
Никита ΠšΠΎΡ€ΠΎΠ»ΡŒ
Никита ΠšΠΎΡ€ΠΎΠ»ΡŒ

πŸ’»
raguenets
raguenets

πŸ’»
robog-two
robog-two

πŸ’»
teddywing
teddywing

πŸ’»
chikuwa
chikuwa

πŸ’»
ΰ² _ΰ² 
ΰ² _ΰ² 

πŸ’»
Abe Pazos
Abe Pazos

πŸ’»
Alex
Alex

πŸ’»
Alexander Hurst
Alexander Hurst

πŸ’»
AnΔ±l
AnΔ±l

πŸ’»
Barış
Barış

πŸ’»
Brian Sapozhnikov
Brian Sapozhnikov

πŸ’»
Carlos Mario Rodriguez Perdomo
Carlos Mario Rodriguez Perdomo

πŸ’»
CyberFlame
CyberFlame

πŸ’»
Dhruv Jawali
Dhruv Jawali

πŸ’»
FlorisVO
FlorisVO

πŸ’»
Frank Leon Rose
Frank Leon Rose

πŸ’»
Greg Borenstein
Greg Borenstein

πŸ’»
Guillermo Perez
Guillermo Perez

πŸ’»
Henning Kiel
Henning Kiel

πŸ’»
J David Eisenberg
J David Eisenberg

πŸ’»
Jordan Ephron
Jordan Ephron

πŸ’»
Jason Sigal
Jason Sigal

πŸ’»
Jordan Orelli
Jordan Orelli

πŸ’»
Kalle
Kalle

πŸ’»
Laureano LΓ³pez
Laureano LΓ³pez

πŸ’»
Lesley Wagner
Lesley Wagner

πŸ’»
Mark Slee
Mark Slee

πŸ’»
MARTIN LEOPOLD GROEDL
MARTIN LEOPOLD GROEDL

πŸ’»
Martin Prout
Martin Prout

πŸ’»
Mathias Herberts
Mathias Herberts

πŸ’»
Diya Solanki
Diya Solanki

πŸš‡
Neil C Smith
Neil C Smith

πŸš‡
kate hollenbach
kate hollenbach

πŸ’» πŸ“¦ πŸ§‘β€πŸ« πŸ›
Rishabdev Tudu
Rishabdev Tudu

πŸ“– πŸ’»
Pau
Pau

πŸ“–
Junology
Junology

πŸ’»
Jaap Meijers
Jaap Meijers

πŸ“–
Xin Xin
Xin Xin

πŸ“‹ πŸ€”
Benjamin Fox
Benjamin Fox

πŸ’»
e1dem
e1dem

πŸ’»
Aditya Chaudhary
Aditya Chaudhary

πŸ’»
Rishab Kumar Jha
Rishab Kumar Jha

πŸ’»
Yehia Rasheed
Yehia Rasheed

πŸ’»
Subhraman Sarkar
Subhraman Sarkar

πŸ’» ️️️️♿️ πŸ“–
SushantBansal-tech
SushantBansal-tech

πŸ€” πŸ’»
Konsl
Konsl

πŸ“–
Mario Guzman
Mario Guzman

πŸ“–
Aranya Dutta
Aranya Dutta

πŸ’»
ovalnine
ovalnine

πŸ’»
Joshua D. Boyd
Joshua D. Boyd

πŸ“–
Vaivaswat Dubey
Vaivaswat Dubey

πŸ’»
jSdCool
jSdCool

πŸ’» πŸ“–
AhmedMaged
AhmedMaged

πŸ’»
Nico Mexis
Nico Mexis

πŸ’»
charlotte 🌸
charlotte 🌸

πŸ‘€
Joackim de Bourqueney
Joackim de Bourqueney

πŸ’»
Tonz
Tonz

πŸ’» πŸ“–
Andrew
Andrew

πŸ’»
Ngoc Doan
Ngoc Doan

πŸ’»
Manoel Ribeiro
Manoel Ribeiro

πŸ“–
Moon
Moon

πŸ’»
Nia Perez
Nia Perez

πŸ’»
SuganthiThomas
SuganthiThomas

πŸ’»
- - - - - +This project follows the [all-contributors specification](https://github.com/all-contributors/all-contributors) and the [Emoji Key](https://all-contributors.github.io/emoji-key/) ✨ for contribution types. Detailed instructions on how to add yourself or add contribution emojis to your name are [here](https://github.com/processing/processing4/issues/839). You can also post an issue or comment on a pull request with the text: `@all-contributors please add @YOUR-USERNAME for THINGS` (where `THINGS` is a comma-separated list of entries from the [list of possible contribution types](https://all-contributors.github.io/emoji-key/)) and our nice bot will add you to [CONTRIBUTORS.md](./CONTRIBUTORS.md) automatically! \ No newline at end of file diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 2551a54d64..41370918ba 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -51,11 +51,15 @@ * files and images, etc.) that comes from that. */ public class Base { - // Added accessors for 0218 because the UpdateCheck class was not properly - // updating the values, due to javac inlining the static final values. + /** + * Revision number, used for update checks and contribution compatibility. + */ static private final int REVISION = Integer.parseInt(System.getProperty("processing.revision", "1295")); - /** This might be replaced by main() if there's a lib/version.txt file. */ - static private String VERSION_NAME = System.getProperty("processing.version", "1295"); //$NON-NLS-1$ + /** + * This might be replaced by main() if there's a lib/version.txt file. + * + */ + static private String VERSION_NAME = System.getProperty("processing.version", "1295"); static final public String SKETCH_BUNDLE_EXT = ".pdez"; static final public String CONTRIB_BUNDLE_EXT = ".pdex"; @@ -65,11 +69,12 @@ public class Base { * if an empty file named 'debug' is found in the settings folder. * See implementation in createAndShowGUI(). */ - static public boolean DEBUG = Boolean.parseBoolean(System.getenv().getOrDefault("DEBUG", "false")); - /** True if running via Commander. */ + /** + * is Processing being run from the command line (true) or from the GUI (false)? + */ static private boolean commandLine; /** @@ -128,105 +133,59 @@ public class Base { static public void main(final String[] args) { Messages.log("Starting Processing version" + VERSION_NAME + " revision "+ REVISION); EventQueue.invokeLater(() -> { - try { - createAndShowGUI(args); + run(args); + }); + } - } catch (Throwable t) { - // Windows Defender has been insisting on destroying each new - // release by removing core.jar and other files. Yay! - // https://github.com/processing/processing/issues/5537 - if (Platform.isWindows()) { - String mess = t.getMessage(); - String missing = null; - if (mess.contains("Could not initialize class com.sun.jna.Native")) { - //noinspection SpellCheckingInspection - missing = "jnidispatch.dll"; - } else if (t instanceof NoClassDefFoundError && - mess.contains("processing/core/PApplet")) { - // Had to change how this was called - // https://github.com/processing/processing4/issues/154 - missing = "core.jar"; - } - if (missing != null) { - Messages.showError("Necessary files are missing", - "A file required by Processing (" + missing + ") is missing.\n\n" + - "Make sure that you're not trying to run Processing from inside\n" + - "the .zip file you downloaded, and check that Windows Defender\n" + - "has not removed files from the Processing folder.\n\n" + - "(Defender sometimes flags parts of Processing as malware.\n" + - "It is not, but Microsoft has ignored our pleas for help.)", t); - } + /** + * The main run() method, wrapped in a try/catch to + * provide a graceful error message if something goes wrong. + */ + private static void run(String[] args) { + try { + createAndShowGUI(args); + } catch (Throwable t) { + // Windows Defender has been insisting on destroying each new + // release by removing core.jar and other files. Yay! + // https://github.com/processing/processing/issues/5537 + if (Platform.isWindows()) { + String mess = t.getMessage(); + String missing = null; + if (mess.contains("Could not initialize class com.sun.jna.Native")) { + //noinspection SpellCheckingInspection + missing = "jnidispatch.dll"; + } else if (t instanceof NoClassDefFoundError && + mess.contains("processing/core/PApplet")) { + // Had to change how this was called + // https://github.com/processing/processing4/issues/154 + missing = "core.jar"; + } + if (missing != null) { + Messages.showError("Necessary files are missing", + "A file required by Processing (" + missing + ") is missing.\n\n" + + "Make sure that you're not trying to run Processing from inside\n" + + "the .zip file you downloaded, and check that Windows Defender\n" + + "has not removed files from the Processing folder.\n\n" + + "(Defender sometimes flags parts of Processing as malware.\n" + + "It is not, but Microsoft has ignored our pleas for help.)", t); } - Messages.showTrace("Unknown Problem", - "A serious error happened during startup. Please report:\n" + - "http://github.com/processing/processing4/issues/new", t, true); } - }); + Messages.showTrace("Unknown Problem", + "A serious error happened during startup. Please report:\n" + + "http://github.com/processing/processing4/issues/new", t, true); + } } static private void createAndShowGUI(String[] args) { - // these times are fairly negligible relative to Base. -// long t1 = System.currentTimeMillis(); - // TODO: Cleanup old locations if no longer installed - // TODO: Cleanup old locations if current version is installed in the same location - - File versionFile = Platform.getContentFile("lib/version.txt"); - if (versionFile != null && versionFile.exists()) { - String[] lines = PApplet.loadStrings(versionFile); - if (lines != null && lines.length > 0) { - if (!VERSION_NAME.equals(lines[0])) { - VERSION_NAME = lines[0]; - } - } - } + checkVersion(); - // Detect settings.txt in the lib folder for portable versions - File settingsFile = Platform.getContentFile("lib/settings.txt"); - if (settingsFile != null && settingsFile.exists()) { - try { - Settings portable = new Settings(settingsFile); - String path = portable.get("settings.path"); - File folder = new File(path); - boolean success = true; - if (!folder.exists()) { - success = folder.mkdirs(); - if (!success) { - Messages.err("Could not create " + folder + " to store settings."); - } - } - if (success) { - if (!folder.canRead()) { - Messages.err("Cannot read from " + folder); - } else if (!folder.canWrite()) { - Messages.err("Cannot write to " + folder); - } else { - settingsOverride = folder.getAbsoluteFile(); - } - } - } catch (IOException e) { - Messages.err("Error while reading the settings.txt file", e); - } - } + checkPortable(); Platform.init(); // call after Platform.init() because we need the settings folder Console.startup(); - // Set the debug flag based on a file being present in the settings folder - File debugFile = getSettingsFile("debug"); - - // If it's a directory, it's a leftover from much older releases - // (2.x? 3.x?) that wrote DebugMode.log files into this directory. - // Could remove the directory, but it's harmless enough that it's - // not worth deleting files in case something could go wrong. - if (debugFile.exists() && debugFile.isFile()) { - DEBUG = true; - } - - // Use native popups to avoid looking crappy on macOS - JPopupMenu.setDefaultLightWeightPopupEnabled(false); - // Don't put anything above this line that might make GUI, // because the platform has to be inited properly first. @@ -239,8 +198,6 @@ static private void createAndShowGUI(String[] args) { // run static initialization that grabs all the prefs Preferences.init(); -// long t2 = System.currentTimeMillis(); - // boolean flag indicating whether to create new server instance or not boolean createNewInstance = DEBUG || !SingleInstance.alreadyRunning(args); @@ -250,56 +207,26 @@ static private void createAndShowGUI(String[] args) { return; } - if (createNewInstance) { - // Set the look and feel before opening the window - try { - Platform.setLookAndFeel(); - Platform.setInterfaceZoom(); - } catch (Exception e) { - Messages.err("Error while setting up the interface", e); //$NON-NLS-1$ - } -// long t3 = System.currentTimeMillis(); + // Set the look and feel before opening the window + setLookAndFeel(); - // Get the sketchbook path, and make sure it's set properly - locateSketchbookFolder(); + // Get the sketchbook path, and make sure it's set properly + locateSketchbookFolder(); -// long t4 = System.currentTimeMillis(); + // Load colors for UI elements. This must happen after Preferences.init() + // (so that fonts are set) and locateSketchbookFolder() so that a + // theme.txt file in the user's sketchbook folder is picked up. + Theme.init(); - // Load colors for UI elements. This must happen after Preferences.init() - // (so that fonts are set) and locateSketchbookFolder() so that a - // theme.txt file in the user's sketchbook folder is picked up. - Theme.init(); + // Create a location for untitled sketches + setupUntitleSketches(); - // Create a location for untitled sketches - try { - // Users on a shared machine may also share a TEMP folder, - // which can cause naming collisions; use a UUID as the name - // for the subfolder to introduce another layer of indirection. - // https://github.com/processing/processing4/issues/549 - // The UUID also prevents collisions when restarting the - // software. Otherwise, after using up the a-z naming options - // it was not possible for users to restart (without manually - // finding and deleting the TEMP files). - // https://github.com/processing/processing4/issues/582 - String uuid = UUID.randomUUID().toString(); - untitledFolder = new File(Util.getProcessingTemp(), uuid); - - } catch (IOException e) { - Messages.showError("Trouble without a name", - "Could not create a place to store untitled sketches.\n" + - "That's gonna prevent us from continuing.", e); - } - -// long t5 = System.currentTimeMillis(); -// long t6 = 0; // replaced below, just needs decl outside try { } - - Messages.log("About to create Base..."); //$NON-NLS-1$ - try { + Messages.log("About to create Base..."); + try { final Base base = new Base(args); base.updateTheme(); Messages.log("Base() constructor succeeded"); -// t6 = System.currentTimeMillis(); // Prevent more than one copy of the PDE from running. SingleInstance.startServer(base); @@ -308,7 +235,7 @@ static private void createAndShowGUI(String[] args) { handleCrustyDisplay(); handleTempCleaning(); - } catch (Throwable t) { + } catch (Throwable t) { // Catch-all to pick up badness during startup. Throwable err = t; if (t.getCause() != null) { @@ -317,24 +244,51 @@ static private void createAndShowGUI(String[] args) { err = t.getCause(); } Messages.showTrace("We're off on the wrong foot", - "An error occurred during startup.", err, true); - } - Messages.log("Done creating Base..."); //$NON-NLS-1$ + "An error occurred during startup.", err, true); + } + Messages.log("Done creating Base..."); + } + + private static void setupUntitleSketches() { + try { + // Users on a shared machine may also share a TEMP folder, + // which can cause naming collisions; use a UUID as the name + // for the subfolder to introduce another layer of indirection. + // https://github.com/processing/processing4/issues/549 + // The UUID also prevents collisions when restarting the + // software. Otherwise, after using up the a-z naming options + // it was not possible for users to restart (without manually + // finding and deleting the TEMP files). + // https://github.com/processing/processing4/issues/582 + String uuid = UUID.randomUUID().toString(); + untitledFolder = new File(Util.getProcessingTemp(), uuid); -// long t10 = System.currentTimeMillis(); -// System.out.println("startup took " + (t2-t1) + " " + (t3-t2) + " " + (t4-t3) + " " + (t5-t4) + " " + (t6-t5) + " " + (t10-t6) + " ms"); + } catch (IOException e) { + Messages.showError("Trouble without a name", + "Could not create a place to store untitled sketches.\n" + + "That's gonna prevent us from continuing.", e); } } + private static void setLookAndFeel() { + try { + // Use native popups to avoid looking crappy on macOS + JPopupMenu.setDefaultLightWeightPopupEnabled(false); + + Platform.setLookAndFeel(); + Platform.setInterfaceZoom(); + } catch (Exception e) { + Messages.err("Error while setting up the interface", e); //$NON-NLS-1$ + } + } + public void updateTheme() { try { - //System.out.println("updating theme"); FlatLaf laf = "dark".equals(Theme.get("laf.mode")) ? new FlatDarkLaf() : new FlatLightLaf(); laf.setExtraDefaults(Collections.singletonMap("@accentColor", Theme.get("laf.accent.color"))); - //System.out.println(laf.getExtraDefaults()); //UIManager.setLookAndFeel(laf); FlatLaf.setup(laf); // updateUI() will wipe out our custom components @@ -449,6 +403,54 @@ static public void cleanTempFolders() { } } + /** + * Check for a version.txt file in the lib folder to override + */ + private static void checkVersion() { + File versionFile = Platform.getContentFile("lib/version.txt"); + if (versionFile != null && versionFile.exists()) { + String[] lines = PApplet.loadStrings(versionFile); + if (lines != null && lines.length > 0) { + if (!VERSION_NAME.equals(lines[0])) { + VERSION_NAME = lines[0]; + } + } + } + } + + /** + * Check for portable settings.txt file in the lib folder + * to override the location of the settings folder. + */ + static void checkPortable() { + // Detect settings.txt in the lib folder for portable versions + File settingsFile = Platform.getContentFile("lib/settings.txt"); + if (settingsFile != null && settingsFile.exists()) { + try { + Settings portable = new Settings(settingsFile); + String path = portable.get("settings.path"); + File folder = new File(path); + boolean success = true; + if (!folder.exists()) { + success = folder.mkdirs(); + if (!success) { + Messages.err("Could not create " + folder + " to store settings."); + } + } + if (success) { + if (!folder.canRead()) { + Messages.err("Cannot read from " + folder); + } else if (!folder.canWrite()) { + Messages.err("Cannot write to " + folder); + } else { + settingsOverride = folder.getAbsoluteFile(); + } + } + } catch (IOException e) { + Messages.err("Error while reading the settings.txt file", e); + } + } + } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -484,44 +486,21 @@ static public boolean isCommandLine() { public Base(String[] args) throws Exception { - long t1 = System.currentTimeMillis(); ContributionManager.init(this); - long t2 = System.currentTimeMillis(); buildCoreModes(); - long t2b = System.currentTimeMillis(); rebuildContribModes(); - long t2c = System.currentTimeMillis(); rebuildContribExamples(); - long t3 = System.currentTimeMillis(); // Needs to happen after the sketchbook folder has been located. // Also relies on the modes to be loaded, so it knows what can be // marked as an example. Recent.init(this); - long t4 = System.currentTimeMillis(); - String lastModeIdentifier = Preferences.get("mode.last"); //$NON-NLS-1$ - if (lastModeIdentifier == null) { - nextMode = getDefaultMode(); - Messages.log("Nothing set for last.sketch.mode, using default."); //$NON-NLS-1$ - } else { - for (Mode m : getModeList()) { - if (m.getIdentifier().equals(lastModeIdentifier)) { - Messages.logf("Setting next mode to %s.", lastModeIdentifier); //$NON-NLS-1$ - nextMode = m; - } - } - if (nextMode == null) { - nextMode = getDefaultMode(); - Messages.logf("Could not find mode %s, using default.", lastModeIdentifier); //$NON-NLS-1$ - } - } + setupNextMode(); //contributionManagerFrame = new ContributionManagerDialog(); - long t5 = System.currentTimeMillis(); - // Make sure ThinkDifferent has library examples too nextMode.rebuildLibraryList(); @@ -529,10 +508,20 @@ public Base(String[] args) throws Exception { // menu works on Mac OS X (since it needs examplesFolder to be set). Platform.initBase(this); - long t6 = System.currentTimeMillis(); + // check for updates + UpdateCheck.doCheck(this); + -// // Check if there were previously opened sketches to be restored -// boolean opened = restoreSketches(); + ContributionListing cl = ContributionListing.getInstance(); + cl.downloadAvailableList(this, new ContribProgress(null)); + + openFilesOrNew(args); + + } + + private void openFilesOrNew(String[] args) { + // Check if there were previously opened sketches to be restored + // boolean opened = restoreSketches(); boolean opened = false; // Check if any files were passed in on the command line @@ -558,8 +547,6 @@ public Base(String[] args) throws Exception { } } - long t7 = System.currentTimeMillis(); - // Create a new empty window (will be replaced with any files to be opened) if (!opened) { Messages.log("Calling handleNew() to open a new window"); @@ -567,22 +554,25 @@ public Base(String[] args) throws Exception { } else { Messages.log("No handleNew(), something passed on the command line"); } + } - long t8 = System.currentTimeMillis(); - - // check for updates - new UpdateCheck(this); - - ContributionListing cl = ContributionListing.getInstance(); - cl.downloadAvailableList(this, new ContribProgress(null)); - long t9 = System.currentTimeMillis(); - - Messages.log("core modes: " + (t2b-t2) + - ", contrib modes: " + (t2c-t2b) + - ", contrib ex: " + (t2c-t2b)); - Messages.log("base took " + (t2-t1) + " " + (t3-t2) + " " + (t4-t3) + - " " + (t5-t4) + " t6-t5=" + (t6-t5) + " " + (t7-t6) + - " handleNew=" + (t8-t7) + " " + (t9-t8) + " ms"); + private void setupNextMode() { + String lastModeIdentifier = Preferences.get("mode.last"); //$NON-NLS-1$ + if (lastModeIdentifier == null) { + nextMode = getDefaultMode(); + Messages.log("Nothing set for last.sketch.mode, using default."); //$NON-NLS-1$ + } else { + for (Mode m : getModeList()) { + if (m.getIdentifier().equals(lastModeIdentifier)) { + Messages.logf("Setting next mode to %s.", lastModeIdentifier); //$NON-NLS-1$ + nextMode = m; + } + } + if (nextMode == null) { + nextMode = getDefaultMode(); + Messages.logf("Could not find mode %s, using default.", lastModeIdentifier); //$NON-NLS-1$ + } + } } diff --git a/app/src/processing/app/Processing.kt b/app/src/processing/app/Processing.kt index 6bc6b64a7e..08ad763775 100644 --- a/app/src/processing/app/Processing.kt +++ b/app/src/processing/app/Processing.kt @@ -19,7 +19,14 @@ import java.util.prefs.Preferences import kotlin.concurrent.thread - +/** + * This function is the new modern entry point for Processing + * It uses Clikt to provide a command line interface with subcommands + * + * If you want to add new functionality to the CLI, create a new subcommand + * and add it to the list of subcommands below. + * + */ suspend fun main(args: Array){ Processing() .subcommands( @@ -32,6 +39,10 @@ suspend fun main(args: Array){ .main(args) } +/** + * The main Processing command, will open the ide if no subcommand is provided + * Will also launch the `updateInstallLocations` function in a separate thread + */ class Processing: SuspendingCliktCommand("processing"){ val version by option("-v","--version") .flag() @@ -61,7 +72,10 @@ class Processing: SuspendingCliktCommand("processing"){ } } - +/** + * A command to start the Processing Language Server + * This is used by IDEs to provide language support for Processing sketches + */ class LSP: SuspendingCliktCommand("lsp"){ override fun help(context: Context) = "Start the Processing Language Server" override suspend fun run(){ @@ -79,6 +93,11 @@ class LSP: SuspendingCliktCommand("lsp"){ } } +/** + * A command to invoke the legacy CLI of Processing + * This is mainly for backwards compatibility with existing scripts + * that use the old CLI interface + */ class LegacyCLI(val args: Array): SuspendingCliktCommand("cli") { override val treatUnknownOptionsAsArgs = true @@ -99,6 +118,16 @@ class LegacyCLI(val args: Array): SuspendingCliktCommand("cli") { } } +/** + * Update the install locations in preferences + * The install locations are stored in the preferences as a comma separated list of paths + * Each path is followed by a caret (^) and the version of Processing at that location + * This is used by other programs to find all installed versions of Processing + * works from 4.4.6 onwards + * + * Example: + * /path/to/processing-4.0^4.0,/path/to/processing-3.5.4^3.5.4 + */ fun updateInstallLocations(){ val preferences = Preferences.userRoot().node("org/processing/app") val installLocations = preferences.get("installLocations", "") diff --git a/app/src/processing/app/UpdateCheck.java b/app/src/processing/app/UpdateCheck.java index e18daee3eb..20c91dd38c 100644 --- a/app/src/processing/app/UpdateCheck.java +++ b/app/src/processing/app/UpdateCheck.java @@ -63,6 +63,9 @@ public class UpdateCheck { static private final long ONE_DAY = 24 * 60 * 60 * 1000; + public static void doCheck(Base base) { + new UpdateCheck(base); + } public UpdateCheck(Base base) { this.base = base; diff --git a/app/src/processing/app/contrib/ContributionManager.java b/app/src/processing/app/contrib/ContributionManager.java index c4d45f7d7d..79a7b54eb3 100644 --- a/app/src/processing/app/contrib/ContributionManager.java +++ b/app/src/processing/app/contrib/ContributionManager.java @@ -694,15 +694,9 @@ static private void clearRestartFlags(File root) { static public void init(Base base) throws Exception { -// long t1 = System.currentTimeMillis(); - // Moved here to make sure it runs on EDT [jv 170121] contribListing = ContributionListing.getInstance(); -// long t2 = System.currentTimeMillis(); managerFrame = new ManagerFrame(base); -// long t3 = System.currentTimeMillis(); cleanup(base); -// long t4 = System.currentTimeMillis(); -// System.out.println("ContributionManager.init() " + (t2-t1) + " " + (t3-t2) + " " + (t4-t3)); } diff --git a/app/src/processing/app/contrib/ManagerFrame.java b/app/src/processing/app/contrib/ManagerFrame.java index ab68fd1db5..bc15a439e8 100644 --- a/app/src/processing/app/contrib/ManagerFrame.java +++ b/app/src/processing/app/contrib/ManagerFrame.java @@ -61,27 +61,15 @@ public class ManagerFrame { public ManagerFrame(Base base) { this.base = base; - // TODO Optimize these inits... unfortunately it needs to run on the EDT, - // and Swing is a piece of s*t, so it's gonna be slow with lots of contribs. - // In particular, load everything and then fire the update events. - // Also, don't pull all the colors over and over again. -// long t1 = System.currentTimeMillis(); librariesTab = new ContributionTab(this, ContributionType.LIBRARY); -// long t2 = System.currentTimeMillis(); modesTab = new ContributionTab(this, ContributionType.MODE); -// long t3 = System.currentTimeMillis(); toolsTab = new ContributionTab(this, ContributionType.TOOL); -// long t4 = System.currentTimeMillis(); examplesTab = new ContributionTab(this, ContributionType.EXAMPLES); -// long t5 = System.currentTimeMillis(); updatesTab = new UpdateContributionTab(this); -// long t6 = System.currentTimeMillis(); tabList = new ContributionTab[] { librariesTab, modesTab, toolsTab, examplesTab, updatesTab }; - -// System.out.println("ManagerFrame. " + (t2-t1) + " " + (t3-t2) + " " + (t4-t3) + " " + (t5-t4) + " " + (t6-t5)); } diff --git a/app/src/processing/app/syntax/README.md b/app/src/processing/app/syntax/README.md index 04e7bdc328..aabe0c2e24 100644 --- a/app/src/processing/app/syntax/README.md +++ b/app/src/processing/app/syntax/README.md @@ -1,4 +1,16 @@ -# πŸ‰ Fixing this code: here be dragons. πŸ‰ +# Replacing our custom version of JEditTextArea + +Since 2025 we have started a migration of Swing to Jetpack Compose and we will eventually need to replace the JEditTextArea as well. + +I think a good current strategy would be to start using `RSyntaxTextArea` for an upcoming p5.js mode. `RSyntaxTextArea` is a better maintained and well rounded library. As noted below, a lot of the current state management of the PDE is interetwined with the JEditTextArea implementation. This will force us to decouple the state management out of the `JEditTextArea` whilst also trying to keep backwards compatibility alive for Tweak Mode and the current implementation of autocomplete. + +I also did some more research into the potential of using a JS + LSP based editor within Jetpack Compose but as of writing (early 2025) the only way to do so would be to embed chromium into the PDE through something like [Java-CEF]([url](https://github.com/chromiumembedded/java-cef)) and it looks like a PoC for Jetpack Compose Desktop exists [here](https://github.com/JetBrains/compose-multiplatform/blob/9cd413a4ed125bee5b624550fbd40a05061e912a/experimental/cef/src/main/kotlin/org/jetbrains/compose/desktop/browser/BrowserView.kt). Moving the entire PDE into an electron app would be essentially a rewrite which currrently is not the target. + +Considering the current direction of the build-in LSP within Processing, I would say that creating a LSP based editor would be a good strategy going forward. + +Research needs to be done on how much the Tweak Mode and autocompletion are _actually_ being used. Currently both these features are quite hidden and I suspect that most users actually move on to more advanced use-cases before they even discover such things. I would like to make both of these features much more prominent within the PDE to test if they are a good value add. + +### Ben Fry's notes Every few years, we've looked at replacing this package with [RSyntaxArea](https://github.com/bobbylight/RSyntaxTextArea), most recently with two attempts during the course of developing [Processing 4](https://github.com/processing/processing4/wiki/Processing-4), but probably dating back to the mid-2000s. diff --git a/build.gradle.kts b/build.gradle.kts index 8e7ad44a7a..dd3df4f710 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,6 +10,7 @@ plugins { // Set the build directory to not /build to prevent accidental deletion through the clean action // Can be deleted after the migration to Gradle is complete + layout.buildDirectory = file(".build") // Configure the dependencyUpdates task @@ -27,4 +28,4 @@ tasks { isNonStable(candidate.version) && !isNonStable(currentVersion) } } -} +} \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 6708e269dc..f4e1ceb607 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -11,18 +11,18 @@ repositories { maven { url = uri("https://jogamp.org/deployment/maven") } } -sourceSets{ - main{ - java{ +sourceSets { + main { + java { srcDirs("src") } - resources{ + resources { srcDirs("src") exclude("**/*.java") } } - test{ - java{ + test { + java { srcDirs("test") } } @@ -33,13 +33,32 @@ dependencies { implementation(libs.gluegen) testImplementation(libs.junit) + testImplementation(libs.junitJupiter) + testImplementation(libs.junitJupiterParams) + testImplementation(libs.junitPlatformSuite) + testImplementation(libs.assertjCore) } -mavenPublishing{ +// Simple JUnit 5 configuration - let JUnit handle everything +tasks.test { + useJUnitPlatform() // JUnit discovers and runs all tests + + // Only configuration, not orchestration + outputs.upToDateWhen { false } + maxParallelForks = 1 + + testLogging { + events("passed", "skipped", "failed", "started") + showStandardStreams = true + } +} + +mavenPublishing { publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = true) + signAllPublications() - pom{ + pom { name.set("Processing Core") description.set("Processing Core") url.set("https://processing.org") @@ -59,7 +78,7 @@ mavenPublishing{ name.set("Ben Fry") } } - scm{ + scm { url.set("https://github.com/processing/processing4") connection.set("scm:git:git://github.com/processing/processing4.git") developerConnection.set("scm:git:ssh://git@github.com/processing/processing4.git") @@ -67,13 +86,9 @@ mavenPublishing{ } } - -tasks.test { - useJUnit() -} tasks.withType { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } -tasks.compileJava{ +tasks.compileJava { options.encoding = "UTF-8" -} +} \ No newline at end of file diff --git a/core/test/processing/visual/__screenshots__/shapes-3d/per-vertex-fills-linux.png b/core/test/processing/visual/__screenshots__/shapes-3d/per-vertex-fills-linux.png new file mode 100644 index 0000000000..608b7ffe20 Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes-3d/per-vertex-fills-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes-3d/per-vertex-strokes-linux.png b/core/test/processing/visual/__screenshots__/shapes-3d/per-vertex-strokes-linux.png new file mode 100644 index 0000000000..7270c15323 Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes-3d/per-vertex-strokes-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes-3d/vertex-coordinates-linux.png b/core/test/processing/visual/__screenshots__/shapes-3d/vertex-coordinates-linux.png new file mode 100644 index 0000000000..e07fe529c8 Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes-3d/vertex-coordinates-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/bezier-curves-linux.png b/core/test/processing/visual/__screenshots__/shapes/bezier-curves-linux.png new file mode 100644 index 0000000000..e628f40541 Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/bezier-curves-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/closed-curves-linux.png b/core/test/processing/visual/__screenshots__/shapes/closed-curves-linux.png new file mode 100644 index 0000000000..2851f61f95 Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/closed-curves-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/closed-polylines-linux.png b/core/test/processing/visual/__screenshots__/shapes/closed-polylines-linux.png new file mode 100644 index 0000000000..43a6cc68ba Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/closed-polylines-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/contours-linux.png b/core/test/processing/visual/__screenshots__/shapes/contours-linux.png new file mode 100644 index 0000000000..032c60a753 Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/contours-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/curves-linux.png b/core/test/processing/visual/__screenshots__/shapes/curves-linux.png new file mode 100644 index 0000000000..5a629e80fb Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/curves-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/curves-tightness-linux.png b/core/test/processing/visual/__screenshots__/shapes/curves-tightness-linux.png new file mode 100644 index 0000000000..aecfee50ea Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/curves-tightness-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/lines-linux.png b/core/test/processing/visual/__screenshots__/shapes/lines-linux.png new file mode 100644 index 0000000000..f2e42539e6 Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/lines-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/points-linux.png b/core/test/processing/visual/__screenshots__/shapes/points-linux.png new file mode 100644 index 0000000000..d0aecf8d30 Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/points-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/polylines-linux.png b/core/test/processing/visual/__screenshots__/shapes/polylines-linux.png new file mode 100644 index 0000000000..8f0a6ad363 Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/polylines-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/quad-strips-linux.png b/core/test/processing/visual/__screenshots__/shapes/quad-strips-linux.png new file mode 100644 index 0000000000..a0836a3a6d Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/quad-strips-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/quadratic-beziers-linux.png b/core/test/processing/visual/__screenshots__/shapes/quadratic-beziers-linux.png new file mode 100644 index 0000000000..85416ec263 Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/quadratic-beziers-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/quads-linux.png b/core/test/processing/visual/__screenshots__/shapes/quads-linux.png new file mode 100644 index 0000000000..b7d2c80f9e Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/quads-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/single-closed-contour-linux.png b/core/test/processing/visual/__screenshots__/shapes/single-closed-contour-linux.png new file mode 100644 index 0000000000..401b9974d1 Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/single-closed-contour-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/single-unclosed-contour-linux.png b/core/test/processing/visual/__screenshots__/shapes/single-unclosed-contour-linux.png new file mode 100644 index 0000000000..401b9974d1 Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/single-unclosed-contour-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/triangle-fans-linux.png b/core/test/processing/visual/__screenshots__/shapes/triangle-fans-linux.png new file mode 100644 index 0000000000..c7c2b87e64 Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/triangle-fans-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/triangle-strips-linux.png b/core/test/processing/visual/__screenshots__/shapes/triangle-strips-linux.png new file mode 100644 index 0000000000..14ee9cd38e Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/triangle-strips-linux.png differ diff --git a/core/test/processing/visual/__screenshots__/shapes/triangles-linux.png b/core/test/processing/visual/__screenshots__/shapes/triangles-linux.png new file mode 100644 index 0000000000..0e25fdbc03 Binary files /dev/null and b/core/test/processing/visual/__screenshots__/shapes/triangles-linux.png differ diff --git a/core/test/processing/visual/src/core/BaselineManager.java b/core/test/processing/visual/src/core/BaselineManager.java new file mode 100644 index 0000000000..93123ab13c --- /dev/null +++ b/core/test/processing/visual/src/core/BaselineManager.java @@ -0,0 +1,38 @@ +package processing.visual.src.core; + +import processing.core.PImage; + +import java.util.List; + +// Baseline manager for updating reference images +public class BaselineManager { + private VisualTestRunner tester; + + public BaselineManager(VisualTestRunner tester) { + this.tester = tester; + } + + public void updateBaseline(String testName, ProcessingSketch sketch, TestConfig config) { + System.out.println("Updating baseline for: " + testName); + + // Capture new image + SketchRunner runner = new SketchRunner(sketch, config); + runner.run(); + PImage newImage = runner.getImage(); + + // Save as baseline + String baselinePath = "__screenshots__/" + + testName.replaceAll("[^a-zA-Z0-9-_]", "-") + + "-" + detectPlatform() + ".png"; + newImage.save(baselinePath); + + System.out.println("Baseline updated: " + baselinePath); + } + + private String detectPlatform() { + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("mac")) return "darwin"; + if (os.contains("win")) return "win32"; + return "linux"; + } +} diff --git a/core/test/processing/visual/src/core/ProcessingSketch.java b/core/test/processing/visual/src/core/ProcessingSketch.java new file mode 100644 index 0000000000..e4750490b6 --- /dev/null +++ b/core/test/processing/visual/src/core/ProcessingSketch.java @@ -0,0 +1,9 @@ +package processing.visual.src.core; + +import processing.core.PApplet; + +// Interface for user sketches +public interface ProcessingSketch { + void setup(PApplet p); + void draw(PApplet p); +} diff --git a/core/test/processing/visual/src/core/TestConfig.java b/core/test/processing/visual/src/core/TestConfig.java new file mode 100644 index 0000000000..fd39bb91e7 --- /dev/null +++ b/core/test/processing/visual/src/core/TestConfig.java @@ -0,0 +1,33 @@ +package processing.visual.src.core; + +// Test configuration class +public class TestConfig { + public int width = 800; + public int height = 600; + public int[] backgroundColor = {255, 255, 255}; // RGB + public long renderWaitTime = 100; // milliseconds + public double threshold = 0.1; + + public TestConfig() {} + + public TestConfig(int width, int height) { + this.width = width; + this.height = height; + } + + public TestConfig(int width, int height, int[] backgroundColor) { + this.width = width; + this.height = height; + this.backgroundColor = backgroundColor; + } + + public TestConfig setThreshold(double threshold) { + this.threshold = threshold; + return this; + } + + public TestConfig setRenderWaitTime(long waitTime) { + this.renderWaitTime = waitTime; + return this; + } +} diff --git a/core/test/processing/visual/src/core/TestResult.java b/core/test/processing/visual/src/core/TestResult.java new file mode 100644 index 0000000000..6ff7c57ac7 --- /dev/null +++ b/core/test/processing/visual/src/core/TestResult.java @@ -0,0 +1,45 @@ +package processing.visual.src.core; + +// Enhanced test result with detailed information +public class TestResult { + public String testName; + public boolean passed; + public double mismatchRatio; + public String error; + public boolean isFirstRun; + public ComparisonDetails details; + + public TestResult(String testName, ComparisonResult comparison) { + this.testName = testName; + this.passed = comparison.passed; + this.mismatchRatio = comparison.mismatchRatio; + this.isFirstRun = comparison.isFirstRun; + this.details = comparison.details; + } + + public static TestResult createError(String testName, String error) { + TestResult result = new TestResult(); + result.testName = testName; + result.passed = false; + result.error = error; + return result; + } + + private TestResult() {} // For error constructor + + public void printResult() { + System.out.print(testName + ": "); + if (error != null) { + System.out.println("ERROR - " + error); + } else if (isFirstRun) { + System.out.println("BASELINE CREATED"); + } else if (passed) { + System.out.println("PASSED"); + } else { + System.out.println("FAILED (mismatch: " + String.format("%.4f", mismatchRatio * 100) + "%)"); + if (details != null) { + details.printDetails(); + } + } + } +} diff --git a/core/test/processing/visual/src/core/VisualTestRunner.java b/core/test/processing/visual/src/core/VisualTestRunner.java new file mode 100644 index 0000000000..758ff0ec30 --- /dev/null +++ b/core/test/processing/visual/src/core/VisualTestRunner.java @@ -0,0 +1,264 @@ +package processing.visual.src.core; + +import processing.core.*; +import java.io.*; +import java.nio.file.*; +import java.util.*; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; + +// Core visual tester class +public class VisualTestRunner { + + private String screenshotDir; + private PixelMatchingAlgorithm pixelMatcher; + private String platform; + + public VisualTestRunner(PixelMatchingAlgorithm pixelMatcher) { + this.pixelMatcher = pixelMatcher; + this.screenshotDir = "test/processing/visual/__screenshots__"; + this.platform = detectPlatform(); + createDirectoryIfNotExists(screenshotDir); + } + + public VisualTestRunner(PixelMatchingAlgorithm pixelMatcher, String screenshotDir) { + this.pixelMatcher = pixelMatcher; + this.screenshotDir = screenshotDir; + this.platform = detectPlatform(); + createDirectoryIfNotExists(screenshotDir); + } + + // Main test execution method + public TestResult runVisualTest(String testName, ProcessingSketch sketch) { + return runVisualTest(testName, sketch, new TestConfig()); + } + + public TestResult runVisualTest(String testName, ProcessingSketch sketch, TestConfig config) { + try { + System.out.println("Running visual test: " + testName); + + // Capture screenshot from sketch + PImage actualImage = captureSketch(sketch, config); + + // Compare with baseline + ComparisonResult comparison = compareWithBaseline(testName, actualImage, config); + + return new TestResult(testName, comparison); + + } catch (Exception e) { + return TestResult.createError(testName, e.getMessage()); + } + } + + // Capture PImage from Processing sketch + private PImage captureSketch(ProcessingSketch sketch, TestConfig config) { + SketchRunner runner = new SketchRunner(sketch, config); + runner.run(); + return runner.getImage(); + } + + // Compare actual image with baseline + private ComparisonResult compareWithBaseline(String testName, PImage actualImage, TestConfig config) { + String baselinePath = getBaselinePath(testName); + + PImage baselineImage = loadBaseline(baselinePath); + + if (baselineImage == null) { + // First run - save as baseline + saveBaseline(testName, actualImage); + return ComparisonResult.createFirstRun(); + } + + // Use your sophisticated pixel matching algorithm + ComparisonResult result = pixelMatcher.compare(baselineImage, actualImage, config.threshold); + + // Save diff images if test failed + if (!result.passed && result.diffImage != null) { + saveDiffImage(testName, result.diffImage); + } + + return result; + } + + // Save diff image for debugging + private void saveDiffImage(String testName, PImage diffImage) { + String sanitizedName = testName.replaceAll("[^a-zA-Z0-9-_]", "-"); + String diffPath; + if (sanitizedName.contains("/")) { + diffPath = "test/processing/visual/diff_" + sanitizedName.replace("/", "_") + "-" + platform + ".png"; + } else { + diffPath = "test/processing/visual/diff_" + sanitizedName + "-" + platform + ".png"; + } + + File diffFile = new File(diffPath); + diffFile.getParentFile().mkdirs(); + + diffImage.save(diffPath); + System.out.println("Diff image saved: " + diffPath); + } + + // Utility methods + private String detectPlatform() { + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("mac")) return "darwin"; + if (os.contains("win")) return "win32"; + return "linux"; + } + + private void createDirectoryIfNotExists(String dir) { + try { + Files.createDirectories(Paths.get(dir)); + } catch (IOException e) { + System.err.println("Failed to create directory: " + dir); + } + } + + private String getBaselinePath(String testName) { + String sanitizedName = testName.replaceAll("[^a-zA-Z0-9-_/]", "-"); + + return screenshotDir + "/" + sanitizedName + "-" + platform + ".png"; + } + + // Replace loadBaseline method: + private PImage loadBaseline(String path) { + File file = new File(path); + if (!file.exists()) { + System.out.println("loadBaseline: File doesn't exist: " + file.getAbsolutePath()); + return null; + } + + try { + System.out.println("loadBaseline: Loading from " + file.getAbsolutePath()); + + // Use Java ImageIO instead of PApplet + BufferedImage img = ImageIO.read(file); + + if (img == null) { + System.out.println("loadBaseline: ImageIO returned null"); + return null; + } + + // Convert BufferedImage to PImage + PImage pImg = new PImage(img.getWidth(), img.getHeight(), PImage.RGB); + img.getRGB(0, 0, pImg.width, pImg.height, pImg.pixels, 0, pImg.width); + pImg.updatePixels(); + + System.out.println("loadBaseline: βœ“ Loaded " + pImg.width + "x" + pImg.height); + return pImg; + + } catch (Exception e) { + System.err.println("loadBaseline: Error loading image: " + e.getMessage()); + e.printStackTrace(); + return null; + } + } + + // Replace saveBaseline method: + private void saveBaseline(String testName, PImage image) { + String path = getBaselinePath(testName); + + if (image == null) { + System.out.println("saveBaseline: βœ— Image is null!"); + return; + } + + try { + // Convert PImage to BufferedImage + BufferedImage bImg = new BufferedImage(image.width, image.height, BufferedImage.TYPE_INT_RGB); + image.loadPixels(); + bImg.setRGB(0, 0, image.width, image.height, image.pixels, 0, image.width); + + // Create File object and ensure parent directories exist + File outputFile = new File(path); + outputFile.getParentFile().mkdirs(); // This creates nested directories + + // Use Java ImageIO to save + ImageIO.write(bImg, "PNG", outputFile); + + System.out.println("Baseline saved: " + path); + + } catch (Exception e) { + System.err.println("Failed to save baseline: " + path); + e.printStackTrace(); + } + } +} +class SketchRunner extends PApplet { + + private ProcessingSketch userSketch; + private TestConfig config; + private PImage capturedImage; + private volatile boolean rendered = false; + + public SketchRunner(ProcessingSketch userSketch, TestConfig config) { + this.userSketch = userSketch; + this.config = config; + } + + public void settings() { + size(config.width, config.height); + pixelDensity(1); + } + + public void setup() { + noLoop(); + + // Set background if specified + if (config.backgroundColor != null) { + background(config.backgroundColor[0], config.backgroundColor[1], config.backgroundColor[2]); + } + + // Call user setup + userSketch.setup(this); + } + + public void draw() { + if (!rendered) { + userSketch.draw(this); + capturedImage = get(); + rendered = true; + noLoop(); + } + } + + public void run() { + String[] args = {"SketchRunner"}; + PApplet.runSketch(args, this); + + // Simple polling with timeout + int maxWait = 100; // 10 seconds max + int waited = 0; + + while (!rendered && waited < maxWait) { + try { + Thread.sleep(100); + waited++; + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + + // Additional wait time + try { + Thread.sleep(config.renderWaitTime); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + if (surface != null) { + surface.setVisible(false); + } + try { + Thread.sleep(200); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + public PImage getImage() { + return capturedImage; + } +} diff --git a/core/test/processing/visual/src/test/base/VisualTest.java b/core/test/processing/visual/src/test/base/VisualTest.java new file mode 100644 index 0000000000..55804b4acb --- /dev/null +++ b/core/test/processing/visual/src/test/base/VisualTest.java @@ -0,0 +1,61 @@ +package processing.visual.src.test.base; + +import org.junit.jupiter.api.*; +import processing.core.*; +import static org.junit.jupiter.api.Assertions.*; +import processing.visual.src.core.*; +import java.nio.file.*; +import java.io.File; + +/** + * Base class for Processing visual tests using JUnit 5 + */ +public abstract class VisualTest { + + protected static VisualTestRunner testRunner; + protected static ImageComparator comparator; + + @BeforeAll + public static void setupTestRunner() { + PApplet tempApplet = new PApplet(); + comparator = new ImageComparator(tempApplet); + testRunner = new VisualTestRunner(comparator); + + System.out.println("Visual test runner initialized"); + } + + /** + * Helper method to run a visual test + */ + protected void assertVisualMatch(String testName, ProcessingSketch sketch) { + assertVisualMatch(testName, sketch, new TestConfig()); + } + + protected void assertVisualMatch(String testName, ProcessingSketch sketch, TestConfig config) { + TestResult result = testRunner.runVisualTest(testName, sketch, config); + + // Print result for debugging + result.printResult(); + + // Handle different result types + if (result.isFirstRun) { + // First run - baseline created, mark as skipped + Assumptions.assumeTrue(false, "Baseline created for " + testName + ". Run tests again to verify."); + } else if (result.error != null) { + fail("Test error: " + result.error); + } else { + // Assert that the test passed + Assertions.assertTrue(result.passed, + String.format("Visual test '%s' failed with mismatch ratio: %.4f%%", + testName, result.mismatchRatio * 100)); + } + } + + /** + * Update baseline for a specific test (useful for maintenance) + */ + protected void updateBaseline(String testName, ProcessingSketch sketch, TestConfig config) { + BaselineManager manager = new BaselineManager(testRunner); + manager.updateBaseline(testName, sketch, config); + } +} \ No newline at end of file diff --git a/core/test/processing/visual/src/test/shapes/Shape3DTest.java b/core/test/processing/visual/src/test/shapes/Shape3DTest.java new file mode 100644 index 0000000000..7006cf329b --- /dev/null +++ b/core/test/processing/visual/src/test/shapes/Shape3DTest.java @@ -0,0 +1,84 @@ +package processing.visual.src.test.shapes; + +import org.junit.jupiter.api.*; +import processing.core.*; +import processing.visual.src.test.base.VisualTest; +import processing.visual.src.core.ProcessingSketch; +import processing.visual.src.core.TestConfig; + +@Tag("shapes") +@Tag("3d") +@Tag("p3d") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class Shape3DTest extends VisualTest { + + private ProcessingSketch create3DTest(Shape3DCallback callback) { + return new ProcessingSketch() { + @Override + public void setup(PApplet p) { + // P3D mode setup would go here if supported + p.background(200); + p.fill(255); + p.stroke(0); + } + + @Override + public void draw(PApplet p) { + callback.draw(p); + } + }; + } + + @FunctionalInterface + interface Shape3DCallback { + void draw(PApplet p); + } + + @Test + @DisplayName("3D vertex coordinates") + public void test3DVertexCoordinates() { + assertVisualMatch("shapes-3d/vertex-coordinates", create3DTest(p -> { + p.beginShape(PApplet.QUAD_STRIP); + p.vertex(10, 10, 0); + p.vertex(10, 40, -150); + p.vertex(40, 10, 150); + p.vertex(40, 40, 200); + p.endShape(); + }), new TestConfig(50, 50)); + } + + @Test + @DisplayName("Per-vertex fills") + public void testPerVertexFills() { + assertVisualMatch("shapes-3d/per-vertex-fills", create3DTest(p -> { + p.beginShape(PApplet.QUAD_STRIP); + p.fill(0); + p.vertex(10, 10); + p.fill(255, 0, 0); + p.vertex(45, 5); + p.fill(0, 255, 0); + p.vertex(15, 35); + p.fill(255, 255, 0); + p.vertex(40, 45); + p.endShape(); + }), new TestConfig(50, 50)); + } + + @Test + @DisplayName("Per-vertex strokes") + public void testPerVertexStrokes() { + assertVisualMatch("shapes-3d/per-vertex-strokes", create3DTest(p -> { + p.strokeWeight(5); + p.beginShape(PApplet.QUAD_STRIP); + p.stroke(0); + p.vertex(10, 10); + p.stroke(255, 0, 0); + p.vertex(45, 5); + p.stroke(0, 255, 0); + p.vertex(15, 35); + p.stroke(255, 255, 0); + p.vertex(40, 45); + p.endShape(); + }), new TestConfig(50, 50)); + } +} \ No newline at end of file diff --git a/core/test/processing/visual/src/test/shapes/ShapeTest.java b/core/test/processing/visual/src/test/shapes/ShapeTest.java new file mode 100644 index 0000000000..47ae08b5f3 --- /dev/null +++ b/core/test/processing/visual/src/test/shapes/ShapeTest.java @@ -0,0 +1,356 @@ +package processing.visual.src.test.shapes; + +import org.junit.jupiter.api.*; +import processing.core.*; +import processing.visual.src.test.base.VisualTest; +import processing.visual.src.core.ProcessingSketch; +import processing.visual.src.core.TestConfig; + +@Tag("shapes") +@Tag("rendering") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class ShapeTest extends VisualTest { + + // Helper method for common setup + private ProcessingSketch createShapeTest(ShapeDrawingCallback callback) { + return new ProcessingSketch() { + @Override + public void setup(PApplet p) { + p.background(200); + p.fill(255); + p.stroke(0); + } + + @Override + public void draw(PApplet p) { + callback.draw(p); + } + }; + } + + @FunctionalInterface + interface ShapeDrawingCallback { + void draw(PApplet p); + } + + // ========== Polylines ========== + + @Test + @Order(1) + @Tag("polylines") + @DisplayName("Drawing polylines") + public void testPolylines() { + assertVisualMatch("shapes/polylines", createShapeTest(p -> { + p.beginShape(); + p.vertex(10, 10); + p.vertex(15, 40); + p.vertex(40, 35); + p.vertex(25, 15); + p.vertex(15, 25); + p.endShape(); + }), new TestConfig(50, 50)); + } + + @Test + @Order(2) + @Tag("polylines") + @DisplayName("Drawing closed polylines") + public void testClosedPolylines() { + assertVisualMatch("shapes/closed-polylines", createShapeTest(p -> { + p.beginShape(); + p.vertex(10, 10); + p.vertex(15, 40); + p.vertex(40, 35); + p.vertex(25, 15); + p.vertex(15, 25); + p.endShape(PApplet.CLOSE); + }), new TestConfig(50, 50)); + } + + // ========== Contours ========== + + @Test + @Order(3) + @Tag("contours") + @DisplayName("Drawing with contours") + public void testContours() { + assertVisualMatch("shapes/contours", createShapeTest(p -> { + p.beginShape(); + // Outer circle + vertexCircle(p, 15, 15, 10, 1); + + // Inner cutout + p.beginContour(); + vertexCircle(p, 15, 15, 5, -1); + p.endContour(); + + // Second outer shape + p.beginContour(); + vertexCircle(p, 30, 30, 8, -1); + p.endContour(); + + p.endShape(); + }), new TestConfig(50, 50)); + } + + @Test + @Order(4) + @Tag("contours") + @DisplayName("Drawing with a single closed contour") + public void testSingleClosedContour() { + assertVisualMatch("shapes/single-closed-contour", createShapeTest(p -> { + p.beginShape(); + p.vertex(10, 10); + p.vertex(40, 10); + p.vertex(40, 40); + p.vertex(10, 40); + + p.beginContour(); + p.vertex(20, 20); + p.vertex(20, 30); + p.vertex(30, 30); + p.vertex(30, 20); + p.endContour(); + + p.endShape(PApplet.CLOSE); + }), new TestConfig(50, 50)); + } + + @Test + @Order(5) + @Tag("contours") + @DisplayName("Drawing with a single unclosed contour") + public void testSingleUnclosedContour() { + assertVisualMatch("shapes/single-unclosed-contour", createShapeTest(p -> { + p.beginShape(); + p.vertex(10, 10); + p.vertex(40, 10); + p.vertex(40, 40); + p.vertex(10, 40); + + p.beginContour(); + p.vertex(20, 20); + p.vertex(20, 30); + p.vertex(30, 30); + p.vertex(30, 20); + p.endContour(); + + p.endShape(PApplet.CLOSE); + }), new TestConfig(50, 50)); + } + + // ========== Triangle Shapes ========== + + @Test + @Order(6) + @Tag("triangles") + @DisplayName("Drawing triangle fans") + public void testTriangleFans() { + assertVisualMatch("shapes/triangle-fans", createShapeTest(p -> { + p.beginShape(PApplet.TRIANGLE_FAN); + p.vertex(25, 25); + for (int i = 0; i <= 12; i++) { + float angle = PApplet.map(i, 0, 12, 0, PApplet.TWO_PI); + p.vertex(25 + 10 * PApplet.cos(angle), 25 + 10 * PApplet.sin(angle)); + } + p.endShape(); + }), new TestConfig(50, 50)); + } + + @Test + @Order(7) + @Tag("triangles") + @DisplayName("Drawing triangle strips") + public void testTriangleStrips() { + assertVisualMatch("shapes/triangle-strips", createShapeTest(p -> { + p.beginShape(PApplet.TRIANGLE_STRIP); + p.vertex(10, 10); + p.vertex(30, 10); + p.vertex(15, 20); + p.vertex(35, 20); + p.vertex(10, 40); + p.vertex(30, 40); + p.endShape(); + }), new TestConfig(50, 50)); + } + + @Test + @Order(8) + @Tag("triangles") + @DisplayName("Drawing with triangles") + public void testTriangles() { + assertVisualMatch("shapes/triangles", createShapeTest(p -> { + p.beginShape(PApplet.TRIANGLES); + p.vertex(10, 10); + p.vertex(15, 40); + p.vertex(40, 35); + p.vertex(25, 15); + p.vertex(10, 10); + p.vertex(15, 25); + p.endShape(); + }), new TestConfig(50, 50)); + } + + // ========== Quad Shapes ========== + + @Test + @Order(9) + @Tag("quads") + @DisplayName("Drawing quad strips") + public void testQuadStrips() { + assertVisualMatch("shapes/quad-strips", createShapeTest(p -> { + p.beginShape(PApplet.QUAD_STRIP); + p.vertex(10, 10); + p.vertex(30, 10); + p.vertex(15, 20); + p.vertex(35, 20); + p.vertex(10, 40); + p.vertex(30, 40); + p.endShape(); + }), new TestConfig(50, 50)); + } + + @Test + @Order(10) + @Tag("quads") + @DisplayName("Drawing with quads") + public void testQuads() { + assertVisualMatch("shapes/quads", createShapeTest(p -> { + p.beginShape(PApplet.QUADS); + p.vertex(10, 10); + p.vertex(15, 10); + p.vertex(15, 15); + p.vertex(10, 15); + p.vertex(25, 25); + p.vertex(30, 25); + p.vertex(30, 30); + p.vertex(25, 30); + p.endShape(); + }), new TestConfig(50, 50)); + } + + // ========== Curves ========== + + @Test + @Order(11) + @Tag("curves") + @DisplayName("Drawing with curves") + public void testCurves() { + assertVisualMatch("shapes/curves", createShapeTest(p -> { + p.beginShape(); + p.curveVertex(10, 10); + p.curveVertex(15, 40); + p.curveVertex(40, 35); + p.curveVertex(25, 15); + p.curveVertex(15, 25); + p.endShape(); + }), new TestConfig(50, 50)); + } + + @Test + @Order(12) + @Tag("curves") + @DisplayName("Drawing closed curves") + public void testClosedCurves() { + assertVisualMatch("shapes/closed-curves", createShapeTest(p -> { + p.beginShape(); + p.curveVertex(10, 10); + p.curveVertex(15, 40); + p.curveVertex(40, 35); + p.curveVertex(25, 15); + p.curveVertex(15, 25); + p.endShape(PApplet.CLOSE); + }), new TestConfig(50, 50)); + } + + @Test + @Order(13) + @Tag("curves") + @DisplayName("Drawing with curves with tightness") + public void testCurvesWithTightness() { + assertVisualMatch("shapes/curves-tightness", createShapeTest(p -> { + p.curveTightness(-1); + p.beginShape(); + p.curveVertex(10, 10); + p.curveVertex(15, 40); + p.curveVertex(40, 35); + p.curveVertex(25, 15); + p.curveVertex(15, 25); + p.endShape(); + }), new TestConfig(50, 50)); + } + + // ========== Bezier Curves ========== + + @Test + @Order(14) + @Tag("bezier") + @DisplayName("Drawing with bezier curves") + public void testBezierCurves() { + assertVisualMatch("shapes/bezier-curves", createShapeTest(p -> { + p.beginShape(); + p.vertex(10, 10); + p.bezierVertex(10, 40, 40, 40, 40, 10); + p.endShape(); + }), new TestConfig(50, 50)); + } + + @Test + @Order(15) + @Tag("bezier") + @DisplayName("Drawing with quadratic beziers") + public void testQuadraticBeziers() { + assertVisualMatch("shapes/quadratic-beziers", createShapeTest(p -> { + p.beginShape(); + p.vertex(10, 10); + p.quadraticVertex(25, 40, 40, 10); + p.endShape(); + }), new TestConfig(50, 50)); + } + + // ========== Points and Lines ========== + + @Test + @Order(16) + @Tag("primitives") + @DisplayName("Drawing with points") + public void testPoints() { + assertVisualMatch("shapes/points", createShapeTest(p -> { + p.strokeWeight(5); + p.beginShape(PApplet.POINTS); + p.vertex(10, 10); + p.vertex(15, 40); + p.vertex(40, 35); + p.vertex(25, 15); + p.vertex(15, 25); + p.endShape(); + }), new TestConfig(50, 50)); + } + + @Test + @Order(17) + @Tag("primitives") + @DisplayName("Drawing with lines") + public void testLines() { + assertVisualMatch("shapes/lines", createShapeTest(p -> { + p.beginShape(PApplet.LINES); + p.vertex(10, 10); + p.vertex(15, 40); + p.vertex(40, 35); + p.vertex(25, 15); + p.endShape(); + }), new TestConfig(50, 50)); + } + + // ========== Helper Methods ========== + + /** + * Helper method to create a circle using vertices + */ + private void vertexCircle(PApplet p, float x, float y, float r, int direction) { + for (int i = 0; i <= 12; i++) { + float angle = PApplet.map(i, 0, 12, 0, PApplet.TWO_PI) * direction; + p.vertex(x + r * PApplet.cos(angle), y + r * PApplet.sin(angle)); + } + } +} \ No newline at end of file diff --git a/core/test/processing/visual/src/test/suites/ShapesSuite.java b/core/test/processing/visual/src/test/suites/ShapesSuite.java new file mode 100644 index 0000000000..f45e472826 --- /dev/null +++ b/core/test/processing/visual/src/test/suites/ShapesSuite.java @@ -0,0 +1,12 @@ +package processing.visual.src.test.suites; + +import org.junit.platform.suite.api.*; + +@Suite +@SuiteDisplayName("Basic Shapes Visual Tests") +@SelectPackages("processing.visual.src.test.shapes") +@ExcludePackages("processing.visual.src.test.suites") +@IncludeTags("shapes") +public class ShapesSuite { + // Empty class - just holds annotations +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 050502f4ca..4aae1c5a8b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,6 +4,8 @@ compose-plugin = "1.7.1" jogl = "2.5.0" antlr = "4.13.2" jupiter = "5.12.0" +junitPlatform = "1.12.0" +assertj = "3.24.2" [libraries] jogl = { module = "org.jogamp.jogl:jogl-all-main", version.ref = "jogl" } @@ -35,6 +37,8 @@ markdown = { module = "com.mikepenz:multiplatform-markdown-renderer-m2", version markdownJVM = { module = "com.mikepenz:multiplatform-markdown-renderer-jvm", version = "0.31.0" } clikt = { module = "com.github.ajalt.clikt:clikt", version = "5.0.2" } kotlinxSerializationJson = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.6.3" } +junitPlatformSuite = { module = "org.junit.platform:junit-platform-suite", version.ref = "junitPlatform" } +assertjCore = { module = "org.assertj:assertj-core", version.ref = "assertj" } [plugins] jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java index 3fab2c8b17..8e4d023b9c 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -110,8 +110,6 @@ protected JavaEditor(Base base, String path, EditorState state, Mode mode) throws EditorException { super(base, path, state, mode); -// long t1 = System.currentTimeMillis(); - jmode = (JavaMode) mode; debugger = new Debugger(this); @@ -127,8 +125,6 @@ protected JavaEditor(Base base, String path, EditorState state, preprocService = new PreprocService(this.jmode, this.sketch); -// long t5 = System.currentTimeMillis(); - usage = new ShowUsage(this, preprocService); inspect = new InspectMode(this, preprocService, usage); rename = new Rename(this, preprocService, usage); @@ -139,16 +135,12 @@ protected JavaEditor(Base base, String path, EditorState state, errorChecker = new ErrorChecker(this::setProblemList, preprocService); -// long t7 = System.currentTimeMillis(); - for (SketchCode code : getSketch().getCode()) { Document document = code.getDocument(); addDocumentListener(document); } sketchChanged(); -// long t9 = System.currentTimeMillis(); - Toolkit.setMenuMnemonics(textarea.getRightClickPopup()); // ensure completion is hidden when editor loses focus @@ -159,9 +151,6 @@ public void windowLostFocus(WindowEvent e) { public void windowGainedFocus(WindowEvent e) { } }); - -// long t10 = System.currentTimeMillis(); -// System.out.println("java editor was " + (t10-t9) + " " + (t9-t7) + " " + (t7-t5) + " " + (t5-t1)); } diff --git a/settings.gradle.kts b/settings.gradle.kts index 7eacb06877..285a190390 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -11,5 +11,9 @@ include( "java:libraries:net", "java:libraries:pdf", "java:libraries:serial", - "java:libraries:svg", -) \ No newline at end of file + "java:libraries:svg" +) + +include("app:utils") +include(":visual-tests") +