22 Commits

Author SHA1 Message Date
Nayan Sawyer
5135e3ad90 Revise blog link text in README
Updated blog link text for clarity.
2026-03-16 16:22:38 -04:00
Nayan Sawyer
60de46a657 Update README with blog link for B12NumbersV3
Added a link to the blog for more information about B12NumbersV3.
2026-03-16 16:22:22 -04:00
Nayan Sawyer
a586ace0ad Update README.md 2022-10-28 14:03:33 -04:00
Nayan Sawyer
659c6fba50 Update README.md 2022-10-28 14:03:18 -04:00
Nayan Sawyer
2e54b39771 Update README.md 2022-10-28 13:59:37 -04:00
Nayan Sawyer
7311164c47 Update README.md 2022-10-28 13:59:20 -04:00
61616
ff5ac1f410 Text updates v1.0.0.2 2022-10-28 13:49:47 -04:00
61616
439d5c4161 Update zchangelog.pde 2022-10-28 13:35:20 -04:00
Nayan Sawyer
a621b36d5c Create LICENSE 2022-10-28 13:32:42 -04:00
61616
19d7c18233 removed source code archive 2022-05-21 19:35:48 -04:00
61616
84aa477af9 1.0.0.1 - fixed timer end message 2022-05-21 19:33:47 -04:00
Nayan Sawyer
0a7a63c3ba update readme 2022-05-20 16:53:20 -04:00
Nayan Sawyer
1a90ebec25 update readme 2022-05-20 16:52:43 -04:00
61616
f81325dffd 1.0.0.0 - First full release! 2022-05-20 16:46:13 -04:00
61616
8838f763ce 0.2.1.2 - Timer now complete 2022-05-20 14:23:50 -04:00
61616
f160a7fadd 0.2.1.1 - finished stopwatch 2022-05-19 17:35:10 -04:00
61616
ceb9c11c9c 0.2.1.1 - Finished Clock 2022-05-19 15:27:58 -04:00
61616
0ad49da001 0.2.1.0 - base changes and clock stuff 2022-05-18 17:08:14 -04:00
Nayan Sawyer
400eb0e78b Merge pull request #1 from GShadow5/temp
0.2.0.0 - Added expression evaluation
2022-05-13 14:16:31 -04:00
61616
7f83b4db59 0.2.0.0 - Added expression evaluation
project has new dependency of exp4j
2022-05-13 14:14:48 -04:00
Nayan Sawyer
d8297af420 Update README.md 2022-05-12 20:01:13 -04:00
61616
a2034715c2 0.1.5.8 - beta release tweaks 2022-05-12 19:56:19 -04:00
30 changed files with 1364 additions and 918 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
windows-amd64

View File

@@ -1,178 +0,0 @@
class MouseHandler {
private MouseData md;
private LiveMethodRelay[] mrs;
MouseHandler(MouseData _md) {
md = _md;
mrs = new LiveMethodRelay[0];
}
// PASSTHROUGH FOR MOUSE DATA //
float sMouseX(){return md.sMouseX();}
float sMouseY(){return md.sMouseY();}
float pSMouseX(){return md.pSMouseX();}
float pSMouseY(){return md.pSMouseY();}
void frameUpdate(PVector offset, float scale){md.update(offset, scale);}// println(mrs.length + " " + millis());}
void addRelay(LiveMethodRelay r) {
clean();
if(r.getTag() == '\0'){ throw new IllegalArgumentException("MouseHandler only accepts tagged LiveMethodRelays"); }
mrs = (LiveMethodRelay[])append(mrs, r);
}
void clean() {
if (mrs.length == 0) return;
for (int i = mrs.length -1; i >= 0; i--) {
if (!mrs[i].live()) {
mrs[i] = mrs[mrs.length - 1];
mrs = (LiveMethodRelay[])shorten(mrs);
}
}
}
void cascade(char tag, float... data) {
clean();
for (int i = 0; i < mrs.length; i++) {
if(mrs[i].getTag() == tag){
mrs[i].execute(data);
}
}
}
}
class MouseData{
private PVector offset;
private float scale;
private float sMouseX;
private float sMouseY;
private float pSMouseX;
private float pSMouseY;
MouseData(PVector _offset, float _scale){
offset = _offset;
scale = _scale;
sMouseX = (mouseX - offset.x)/scale;
sMouseY = (mouseY - offset.y)/scale;
pSMouseX = (pmouseX - offset.x)/scale;
pSMouseY = (pmouseY - offset.y)/scale;
}
void update(PVector _offset, float _scale){
offset = _offset;
scale = _scale;
sMouseX = (mouseX - offset.x)/scale;
sMouseY = (mouseY - offset.y)/scale;
pSMouseX = (pmouseX - offset.x)/scale;
pSMouseY = (pmouseY - offset.y)/scale;
}
float sMouseX(){return sMouseX;}
float sMouseY(){return sMouseY;}
float pSMouseX(){return pSMouseX;}
float pSMouseY(){return pSMouseY;}
}
class LiveMethodRelay extends MethodRelay {
private boolean live;
private char tag;
LiveMethodRelay(Object obj, String name, char _tag, Class... args) {
super(obj, name, args);
tag = _tag;
live = true;
}
LiveMethodRelay(Object obj, String name, Class... args) {
super(obj, name, args);
tag = '\0';
live = true;
}
char getTag() {return tag;}
void setTag(char t) {tag = t;}
boolean live() {return live;}
void kill() {live = false;}
}
import java.lang.reflect.*;
import java.lang.ref.*;
/**
A class that encapsulates a named method to be invoked.
Quark 2015
see https://forum.processing.org/two/discussion/13093/how-to-call-function-by-string-content.html
Modified to use weak references
*/
public static class MethodRelay {
/** The object to handle the draw event */
private WeakReference reference = null;
//private Object handlerObject = null;
/** The method in drawHandlerObject to execute */
private Method handlerMethod = null;
/** the name of the method to handle the event */
private String handlerMethodName;
/** An array of classes that represent the function
parameters in order */
private Class[] parameters = null;
/**
Register a method that has parameters.
parameter obj the object that contains the method to invoke
parameter name the name of the method
parameter args a comma separated list of
*/
MethodRelay(Object obj, String name, Class... args) {
try {
handlerMethodName = name;
parameters = args;
handlerMethod = obj.getClass().getMethod(handlerMethodName, parameters);
reference = new WeakReference(obj);
}
catch (Exception e) {
println("Unable to find the function -");
print(handlerMethodName + "( ");
if (parameters != null) {
for (Class c : parameters)
print(c.getSimpleName() + " ");
println(")");
}
}
}
/**
Register a method that has no parameters.
parameter obj the object that contains the method to invoke
parameter name the name of the method
*/
MethodRelay(Object obj, String name) {
this(obj, name, (Class[])null);
}
/**
Execute a paramerterless method
*/
void execute() {
execute((Object[])null);
}
/**
Execute a method with parameters
parameter data a comma separated list of values
to be passed to the method
*/
void execute(Object... data) {
if (reference.get() != null) {
try {
handlerMethod.invoke(reference.get(), data);
}
catch (Exception e) {
println("Error on invoke");
}
}
}
}

View File

@@ -1,346 +0,0 @@
abstract interface Number{
abstract PVector getPos();
abstract void display();
}
class B12Digit implements Number{
private byte value;
private PVector refPos;
B12Digit(int _value){
if(_value >= 12 || _value < 0){
throw new IllegalArgumentException("B12Digit only accepts decimal integers 0 through 11 -- " + _value);
}
value = byte(_value);
refPos = new PVector(0,0);
}
B12Digit(char _c){
String valid = "+-*/.:()"; // Defines valid input characters
if(!inStr(valid, _c)){ throw new IllegalArgumentException("B12Char only accepts \'+ - * / . :'"); }
value = byte(_c);
refPos = new PVector(0,0);
}
// SETTERS
B12Digit setRefPos(PVector _refPos){ refPos = _refPos; return this;}
B12Digit setRefPos(float _x, float _y){ refPos = new PVector(_x,_y); return this;}
B12Digit setValue(int _value){ value = byte(_value); return this;}
// GETTERS
PVector getPos(){ return refPos; }
int getValue(){ return value; }
boolean isNum(){return value >= 0 && value < 12; }
// RENDER CHARACTERS
void display(){
pushMatrix();
translate(refPos.x,refPos.y);
strokeWeight(1);
noFill();
stroke(1);
ellipseMode(CORNERS);
switch(value) {
// NUMBERS //
case 0:
line0(); break;
case 1:
line1(); break;
case 2:
line1(); line2(); break;
case 3:
line1(); line2(); line3(); break;
case 4:
line4(); break;
case 5:
line4(); line1(); break;
case 6:
line4(); line1(); line2(); break;
case 7:
line4(); line1(); line2(); line3(); break;
case 8:
line8(); line4(); break;
case 9:
line8(); line4(); line1(); break;
case 10:
line8(); line4(); line1(); line2(); break;
case 11:
line8(); line4(); line1(); line2(); line3(); break;
// CHARACTERS //
case '+':
lineMinus(); linePlus(); break;
case '-':
lineMinus(); break;
case '*':
lineTimes(); break;
case '/':
lineMinus(); dotsDiv(); break;
case '.':
strokeWeight(2); period(); break;
case ':':
strokeWeight(2); colon(); break;
case '(':
parenl(); break;
case ')':
parenr(); break;
}
popMatrix();
}
// Individual shape components to build any B12 number
private void line0(){ quad(4,0,7,-6.5,4,-13,1,-6.5); }// ellipse(0,-13,8,0); }
private void line1(){ line(6,0,9,-10); }
private void line2(){ line(3,-10,6,0); }
private void line3(){ line(0,0,3,-10); }
private void line4(){ line(9,-10,2,-13); }
private void line8(){ line(2,-13,9,-16); }
// Individual shape components to build any B12 character
private void lineTimes(){ line(4,-7,8,-3); line(4,-3,8,-7); }
private void dotsDiv(){ point(6,-8); point(6,-2); }
private void lineMinus(){ line(3,-5,9,-5); }
private void linePlus(){ line(6,-8,6,-2); }
private void period(){ point(5,0); }
private void colon(){ point(5,-2); point(5,-8); }
private void parenl(){ line(5,-13,3,-6.5); line(5,0,3,-6.5); }
private void parenr(){ line(1,-13,3,-6.5); line(1,0,3,-6.5);}
// HELPER FUNCTIONS //
private boolean inStr(String st, char _c){
try{
int x = st.indexOf(_c);
return true;
}
catch (Exception e){
return false;
}
}
}
class B12Int implements Number {
private ArrayList<B12Digit> digits;
private int value;
private PVector pos;
private boolean arrayLoaded;
private boolean inPosition;
private int mode;// Can be RIGHT (39), CENTER (3), or LEFT (37)
private int minLen;
B12Int(int _value){
value = _value;
pos = new PVector(0,0);
arrayLoaded = false;
inPosition = false;
mode = LEFT;
minLen = 0;
}
// GETTERS //
int getValue(){ return value; }
PVector getPos(){ return pos; }
B12Float toFloat(){return new B12Float(float(value)); }
// SETTERS //
B12Int setValue(int _value){ value = _value; arrayLoaded = false; return this;}
B12Int setPos(PVector _pos){ pos = _pos.copy(); inPosition = false; return this;}
B12Int setPos(float _x, float _y){ pos = new PVector(_x, _y); inPosition = false;return this; }
B12Int setMinLen(int i){ minLen = i; return this;}
B12Int setAlignMode(int _mode){
if(_mode == DECIMAL || _mode == LEFT || _mode == RIGHT){ mode = _mode; }
else{ println("Alignment only accepts LEFT, RIGHT, and DECIMAL"); }
return this;
}
void display(){
if(!arrayLoaded){ loadArray(); }
if(!inPosition){ positionDigits(); }
pushMatrix();
translate(pos.x,pos.y);
for(int i = 0; i < digits.size(); i++){
digits.get(i).display();
}
popMatrix();
}
private void positionDigits(){
if(mode == LEFT || mode == DECIMAL){
for(int i = 0; i < digits.size(); i++){
digits.get(i).setRefPos((-12 * (i+1)), 0);
}
}else if(mode == RIGHT){
int count = 0;
for(int i = digits.size() - 1; i >= 0; i--){
digits.get(count).setRefPos((12 * i) + 3, 0);
count++;
}
}
inPosition = true;
}
private void loadArray(){
digits = new ArrayList<B12Digit>();
int mval = abs(value);
if(mval == 0){ digits.add(new B12Digit(0)); }
while(mval != 0){
if(mval < 12){
digits.add(new B12Digit(mval));
mval = 0;
}else{
digits.add(new B12Digit(mval % 12));
mval /= 12;
}
}
if(digits.size() < minLen){
digits.add(new B12Digit(0));
}
if(value < 0){
digits.add(new B12Digit('-'));
}
arrayLoaded = true;
}
}
class B12Float implements Number{
private ArrayList<B12Digit> digits;
private float value;
private PVector pos;
private boolean arrayLoaded;
private boolean inPosition;
private int mode;// Can be RIGHT (39), CENTER (3), or LEFT (37)
private int places;
private int pointPlace;
B12Float(float _value){
value = _value;
pos = new PVector(0,0);
arrayLoaded = false;
inPosition = false;
mode = DECIMAL;
places = 4;
}
float getValue(){ return value; }
PVector getPos(){ return pos; }
int getPlaces(){ return places; }
B12Int toInt(){return new B12Int(int(value));}
B12Float setValue(float _value){ value = _value; arrayLoaded = false; return this;}
B12Float setPos(PVector _pos){ pos = _pos.copy(); inPosition = false;return this; }
B12Float setPos(float _x, float _y){ pos = new PVector(_x, _y); inPosition = false;return this; }
B12Float setPlaces(int _places){
if(_places > 12 || _places < 0){
throw new IllegalArgumentException("B12Float ncan only display to 12 duodecimal points");
}else{
places = _places;
}
return this;
}
B12Float setAlignMode(int _mode){
if(_mode == DECIMAL || _mode == LEFT || _mode == RIGHT){ mode = _mode; }
else{ println("Alignment only accepts LEFT, RIGHT, and DECIMAL"); }
return this;
}
void display(){
if(!arrayLoaded){ loadArray(); }
if(!inPosition){ positionDigits(); }
pushMatrix();
translate(pos.x,pos.y);
for(int i = 0; i < digits.size(); i++){
digits.get(i).display();
}
popMatrix();
}
private void positionDigits(){
int curPos = 0;
int count = 0;
if(mode == LEFT){
for(int i = 0; i < pointPlace; i++){
curPos += -12;
digits.get(i).setRefPos(curPos, 0);
count++;
}
curPos += -8;
digits.get(count).setRefPos(curPos, 0);
count++;
curPos += -6;
digits.get(count).setRefPos(curPos, 0);
count++;
for(int i = count; i < digits.size(); i++){
curPos += -12;
digits.get(i).setRefPos(curPos, 0);
}
}else if(mode == DECIMAL){
curPos = -5;
digits.get(pointPlace).setRefPos(curPos,0);
curPos += -2;
for(int i = pointPlace - 1; i >= 0; i--){
curPos += 12;
digits.get(i).setRefPos(curPos,0);
}
curPos = -2;
for(int i = pointPlace + 1; i < digits.size(); i++){
curPos += -12;
digits.get(i).setRefPos(curPos,0);
}
}else if(mode == RIGHT){
for(int i = digits.size() - 1; i >= 0; i--){
digits.get(count).setRefPos((12 * i) + 3, 0);
count++;
}
}
inPosition = true;
}
private void loadArray(){
digits = new ArrayList<B12Digit>();
B12Digit[] temp = new B12Digit[places];
float mval = abs(value);
int whole = int(mval);
float deci = mval - float(whole);
for(int i = 0; i < places; i++){
deci *= 12;
temp[i] = new B12Digit(int(deci));
deci -= float(int(deci));
}
for(int i = places - 1; i >= 0; i--){
digits.add(temp[i]);
}
pointPlace = digits.size();
digits.add(new B12Digit('.'));
while(whole > 0){
if(whole < 12){
digits.add(new B12Digit(whole));
whole = 0;
}else{
digits.add(new B12Digit(whole % 12));
whole /= 12;
}
}
if(value < 0){
digits.add(new B12Digit('-'));
}
arrayLoaded = true;
inPosition = false;
}
}

View File

@@ -0,0 +1,33 @@
class B12Button extends Button{
/*
A button that can render a B12Digit instead of text
*/
B12Digit digit;
B12Button(MouseHandler _mh, B12Digit _digit){
super(_mh);
digit = _digit;
setData(_digit);
}
// GETTERS AND SETTERS //
B12Digit getDigit(){ return digit; }
B12Button setDigit(B12Digit _digit){ digit = _digit; return this; }
// Add drawing the B12Digit to the display method
@Override
void display(){
super.display();
pushMatrix();
translate(super.pos.x,super.pos.y);
switch(super.mode){
case CORNER: digit.setPos(super.dim.x/2 - 4, super.dim.y - 2);
}
digit.display();
popMatrix();
}
}

121
B12NumbersV3/B12Digit.pde Normal file
View File

@@ -0,0 +1,121 @@
class B12Digit implements Number{
// A sngle displayable base 12 digit
private byte value; // Using a byte allows storing characters natively in addition to numbers 0 through 11
private PVector pos;
private color col;
B12Digit(int _value){
if(_value >= 12 || _value < 0){
throw new IllegalArgumentException("B12Digit only accepts decimal integers 0 through 11 -- " + _value);
}
value = byte(_value);
pos = new PVector(0,0);
col = 0;
}
B12Digit(char _c){
String valid = "+-*/.:()"; // Defines valid input characters. This can be added to in the future so long as the code to display the digit is added below
if(!inStr(valid, _c)){ throw new IllegalArgumentException("B12Char only accepts \'+ - * / . :'"); }
value = byte(_c);
pos = new PVector(0,0);
col = 0;
}
// SETTERS
B12Digit setPos(PVector _pos){ pos = _pos; return this;}
B12Digit setPos(float _x, float _y){ pos = new PVector(_x,_y); return this;}
B12Digit setValue(int _value){ value = byte(_value); return this;}
B12Digit setCol(color _col){col = _col; return this;}
// GETTERS
PVector getPos(){ return pos; }
int getValue(){ return value; }
boolean isNum(){return value >= 0 && value < 12; }
color getCol(){return col;}
// RENDER CHARACTERS
void display(){
pushMatrix();
translate(pos.x,pos.y);
strokeWeight(1);
noFill();
stroke(col);
ellipseMode(CORNERS);
switch(value) {
// NUMBERS //
case 0:
line0(); break;
case 1:
line1(); break;
case 2:
line1(); line2(); break;
case 3:
line1(); line2(); line3(); break;
case 4:
line4(); break;
case 5:
line4(); line1(); break;
case 6:
line4(); line1(); line2(); break;
case 7:
line4(); line1(); line2(); line3(); break;
case 8:
line8(); line4(); break;
case 9:
line8(); line4(); line1(); break;
case 10:
line8(); line4(); line1(); line2(); break;
case 11:
line8(); line4(); line1(); line2(); line3(); break;
// CHARACTERS //
case '+':
lineMinus(); linePlus(); break;
case '-':
lineMinus(); break;
case '*':
lineTimes(); break;
case '/':
lineMinus(); dotsDiv(); break;
case '.':
strokeWeight(2); period(); break;
case ':':
strokeWeight(2); colon(); break;
case '(':
parenl(); break;
case ')':
parenr(); break;
}
popMatrix();
}
// Individual shape components to build any B12 number
private void line0(){ quad(4,0,7,-6.5,4,-13,1,-6.5); }// ellipse(0,-13,8,0); }
private void line1(){ line(6,0,9,-10); }
private void line2(){ line(3,-10,6,0); }
private void line3(){ line(0,0,3,-10); }
private void line4(){ line(9,-10,2,-13); }
private void line8(){ line(2,-13,9,-16); }
// Individual shape components to build any B12 character
private void lineTimes(){ line(4,-7,8,-3); line(4,-3,8,-7); } // multiplication symbol (x)
private void dotsDiv(){ point(6,-8); point(6,-2); } // division symbol
private void lineMinus(){ line(3,-5,9,-5); }
private void linePlus(){ line(6,-8,6,-2); }
private void period(){ point(5,0); }
private void colon(){ point(5,-2); point(5,-8); }
private void parenl(){ line(5,-13,3,-6.5); line(5,0,3,-6.5); }
private void parenr(){ line(1,-13,3,-6.5); line(1,0,3,-6.5);}
// HELPER FUNCTIONS //
private boolean inStr(String st, char _c){ // Simply returns a boolean whether a character is present in a string
try{
int x = st.indexOf(_c);
return true;
}
catch (Exception e){
return false;
}
}
}

View File

@@ -1,4 +1,8 @@
class B12Expression { class B12Expression {
/*
The workhorse of the the calculator functionality, B12Expression handles the digits and math
It stores only an array of digits, and using only that it can evaluate expressions.
*/
private B12Digit[] expression; private B12Digit[] expression;
B12Expression(){ B12Expression(){
@@ -7,10 +11,10 @@ class B12Expression {
// GETTERS // // GETTERS //
B12Digit getDigit(int index){ return expression[index]; } B12Digit getDigit(int index){ return expression[index]; }
int length(){return expression.length;} int length(){return expression == null ? 0 : expression.length;}
// SETTERS // // SETTERS //
B12Expression insertChar(int ind, B12Digit _digit){ B12Expression insertChar(int ind, B12Digit _digit){ // Not currently used, but maybe with a future implementation of a cursor?
expression = (B12Digit[])append(expression, _digit); // Add the new digit expression = (B12Digit[])append(expression, _digit); // Add the new digit
if(ind < expression.length - 1){ // Swap new digit if(ind < expression.length - 1){ // Swap new digit
for(int i = expression.length - 1; i > ind; i--){ // Start at second to last digit for(int i = expression.length - 1; i > ind; i--){ // Start at second to last digit
@@ -28,7 +32,6 @@ class B12Expression {
expression = (B12Digit[])shorten(expression); expression = (B12Digit[])shorten(expression);
return this; return this;
} }
B12Expression clear(){ B12Expression clear(){
expression = new B12Digit[0]; expression = new B12Digit[0];
return this; return this;
@@ -36,11 +39,18 @@ class B12Expression {
void evaluate(){ void evaluate(){
String evalString = parseDigits(); String evalString = parseDigits();
println(evalString); Expression exp = new ExpressionBuilder(evalString).build();
//<>// //<>// expression = new B12Float((float)exp.evaluate()).setPlaces(12).getDigits();
println(exp.evaluate());
dbout = str((float)exp.evaluate());
} }
private String parseDigits(){ private String parseDigits(){
/*
Takes the current state of the digit array, converts it to an expression in base 10, and then outputs that as a string.
It goes through each element of the array, grouping numeral digits to be converted to complete numbers, and sticking
non-numeral character on straight.
*/
String valid = "+*-/"; // valid characters to accept String valid = "+*-/"; // valid characters to accept
String evalString = ""; // gets filled with the expression up for evaluation in base 10 String evalString = ""; // gets filled with the expression up for evaluation in base 10
B12Digit[] cnum = new B12Digit[0]; // used to sum numbers as the array is parsed B12Digit[] cnum = new B12Digit[0]; // used to sum numbers as the array is parsed
@@ -48,12 +58,12 @@ class B12Expression {
// Parse expression[] into a base 10 string that can be evaluated mathematically // Parse expression[] into a base 10 string that can be evaluated mathematically
if(!(expression[expression.length - 1].isNum() || expression[expression.length -1].getValue() == ')' )){throw new IllegalArgumentException("Invalid input");} // check that final character is a number if(!(expression[expression.length - 1].isNum() || expression[expression.length -1].getValue() == ')' )){throw new IllegalArgumentException("Invalid input");} // check that final character is a number or parenthesis
for (int c = expression.length; c >= 0; c--){ for (int c = expression.length; c >= 0; c--){ // For each digit in the array, starting at the end
int i = c - 1; int i = c - 1; // Convert counter to array index (counter starts one higher than the final index of the array)
if (i == -1){ // At the end, add the final number if neccessary if (i == -1){ // At the end of the array, add the final number if neccessary
// add number to string if present // add number to string if present
if(cnum.length != 0 && cfs == false){ if(cnum.length != 0 && cfs == false){
B12Int num = (B12Int)convert(cnum,cfs); B12Int num = (B12Int)convert(cnum,cfs);
@@ -114,6 +124,9 @@ class B12Expression {
// HELPER FUNCTIONS // // HELPER FUNCTIONS //
private Number convert(B12Digit[] cnum, boolean isFloat){ private Number convert(B12Digit[] cnum, boolean isFloat){
/*
Converts an array of digits into a B12Int or B12Float respectively
*/
if(!isFloat){ if(!isFloat){
int out = 0; int out = 0;
for(int i = 0; i < cnum.length; i++){ for(int i = 0; i < cnum.length; i++){

159
B12NumbersV3/B12Float.pde Normal file
View File

@@ -0,0 +1,159 @@
class B12Float implements Number{
// A displayable base 12 float. The display functionality is not currently used in the app
private B12Digit[] digits;
private float value;
private PVector pos;
private boolean arrayLoaded;
private boolean inPosition;
private int mode;// Can be RIGHT (39), CENTER (3), or LEFT (37)
private int places;
private int pointPlace;
B12Float(float _value){
value = _value;
pos = new PVector(0,0);
arrayLoaded = false;
inPosition = false;
mode = DECIMAL; // Not sure if this is still supported, but should def be discontinued
places = 4;
}
// GETTERS //
float getValue(){ return value; }
PVector getPos(){ return pos; }
int getPlaces(){ return places; }
B12Int toInt(){return new B12Int(int(value));}
B12Digit[] getDigits(){
loadArray();
B12Digit[] out = (B12Digit[])reverse(digits);
for(int i = out.length-1; i > 0; i--){
if(out[i].getValue() == '.'){
out = (B12Digit[])shorten(out);
break;
}
if(out[i].getValue() == 0){
out = (B12Digit[])shorten(out);
}
}
return out;
}
// SETTERS //
B12Float setValue(float _value){ value = _value; arrayLoaded = false; return this;}
B12Float setPos(PVector _pos){ pos = _pos.copy(); inPosition = false;return this; }
B12Float setPos(float _x, float _y){ pos = new PVector(_x, _y); inPosition = false;return this; }
B12Float setPlaces(int _places){ // Sets number of (dou)decimal places to display
if(_places > 12 || _places < 0){
throw new IllegalArgumentException("B12Float ncan only display to 12 duodecimal points");
}else{
places = _places;
}
return this;
}
B12Float setAlignMode(int _mode){
if(_mode == DECIMAL || _mode == LEFT || _mode == RIGHT){ mode = _mode; }
else{ println("Alignment only accepts LEFT, RIGHT, and DECIMAL"); }
return this;
}
void display(){
if(!arrayLoaded){ loadArray(); }
if(!inPosition){ positionDigits(); }
pushMatrix();
translate(pos.x,pos.y);
for(int i = 0; i < digits.length; i++){
digits[i].display();
}
popMatrix();
}
/* Honestly, when it comes to the loading and positioning of digits in the number
primitives, I don't remember the details, and since it needs to be rewritten
using a different structure anyway, I am not going to document it heavily. The
only time it is used in this program is loadDigits being called before the
digits are output to the calculator which does its own positioning.
*/
private void positionDigits(){
int curPos = 0;
int count = 0;
if(mode == LEFT){
for(int i = 0; i < pointPlace; i++){
curPos += -12;
digits[i].setPos(curPos, 0);
count++;
}
curPos += -8;
digits[count].setPos(curPos, 0);
count++;
curPos += -6;
digits[count].setPos(curPos, 0);
count++;
for(int i = count; i < digits.length; i++){
curPos += -12;
digits[i].setPos(curPos, 0);
}
}else if(mode == DECIMAL){
curPos = -5;
digits[pointPlace].setPos(curPos,0);
curPos += -2;
for(int i = pointPlace - 1; i >= 0; i--){
curPos += 12;
digits[i].setPos(curPos,0);
}
curPos = -2;
for(int i = pointPlace + 1; i < digits.length; i++){
curPos += -12;
digits[i].setPos(curPos,0);
}
}else if(mode == RIGHT){
for(int i = digits.length - 1; i >= 0; i--){
digits[count].setPos((12 * i) + 3, 0);
count++;
}
}
inPosition = true;
}
private void loadArray(){
//digits = new ArrayList<B12Digit>();
digits = new B12Digit[0];
B12Digit[] temp = new B12Digit[places];
float mval = abs(value);
int whole = int(mval);
float deci = mval - float(whole);
for(int i = 0; i < places; i++){
deci *= 12;
temp[i] = new B12Digit(int(deci));
deci -= float(int(deci));
}
for(int i = places - 1; i >= 0; i--){
digits = (B12Digit[])append(digits,temp[i]);
}
pointPlace = digits.length;
digits = (B12Digit[])append(digits,new B12Digit('.'));
while(whole > 0){
if(whole < 12){
digits = (B12Digit[])append(digits,new B12Digit(whole));
whole = 0;
}else{
digits = (B12Digit[])append(digits,new B12Digit(whole % 12));
whole /= 12;
}
}
if(value < 0){
digits = (B12Digit[])append(digits,new B12Digit('-'));
}
arrayLoaded = true;
inPosition = false;
}
}

96
B12NumbersV3/B12Int.pde Normal file
View File

@@ -0,0 +1,96 @@
class B12Int implements Number {
// A displayable base 12 integer. The display functionality is not currently used in the app
private B12Digit[] digits;
private int value;
private PVector pos;
private boolean arrayLoaded; // these bools store the state of the digit array. May no no longer be neccessary
private boolean inPosition;
private int mode;// Can be RIGHT (39), CENTER (3), or LEFT (37)
private int minLen;
B12Int(int _value){
value = _value;
pos = new PVector(0,0);
arrayLoaded = false;
inPosition = false;
mode = LEFT;
minLen = 0;
}
// GETTERS //
int getValue(){ return value; }
PVector getPos(){ return pos; }
B12Float toFloat(){return new B12Float(float(value)); }
B12Digit[] getDigits(){ // Returns digits as an array. Used by calculator
loadArray();
B12Digit[] out = digits;
return out;
}
// SETTERS //
B12Int setValue(int _value){ value = _value; arrayLoaded = false; return this;}
B12Int setPos(PVector _pos){ pos = _pos.copy(); inPosition = false; return this;}
B12Int setPos(float _x, float _y){ pos = new PVector(_x, _y); inPosition = false;return this; }
B12Int setMinLen(int i){ minLen = i; return this;}
B12Int setAlignMode(int _mode){
if(_mode == DECIMAL || _mode == LEFT || _mode == RIGHT){ mode = _mode; }
else{ println("Alignment only accepts LEFT, RIGHT, and DECIMAL"); }
return this;
}
void display(){
if(!arrayLoaded){ loadArray(); }
if(!inPosition){ positionDigits(); }
pushMatrix();
translate(pos.x,pos.y);
for(int i = 0; i < digits.length; i++){
digits[i].display();
}
popMatrix();
}
/* Honestly, when it comes to the loading and positioning of digits in the number
primitives, I don't remember the details, and since it needs to be rewritten
using a different structure anyway, I am not going to document it heavily. The
only time it is used in this program is loadDigits being called before the
digits are output to the calculator which does its own positioning.
*/
private void positionDigits(){ // Sets the position of each individual digit so the number renders properly in relation to its position
if(mode == LEFT || mode == CENTER){
for(int i = 0; i < digits.length; i++){
digits[i].setPos((-12 * (i+1)), 0);
}
}else if(mode == RIGHT){
int count = 0;
for(int i = digits.length - 1; i >= 0; i--){
digits[count].setPos((12 * i) + 3, 0);
count++;
}
}
inPosition = true;
}
private void loadArray(){ // Loads digits into the digit array converting value to base 12 in the process
digits = new B12Digit[0];
int mval = abs(value);
if(mval == 0){ digits = (B12Digit[])append(digits, new B12Digit(0)); }
while(mval != 0){
if(mval < 12){
digits = (B12Digit[])append(digits, new B12Digit(mval));
mval = 0;
}else{
digits = (B12Digit[])append(digits, new B12Digit(mval % 12));
mval /= 12;
}
}
if(digits.length < minLen){
digits = (B12Digit[])append(digits, new B12Digit(0));
}
if(value < 0){
digits = (B12Digit[])append(digits, new B12Digit('-'));
}
arrayLoaded = true;
}
}

View File

@@ -1,45 +1,67 @@
/**
* Base 12 Toolkit
* Nayan Sawyer
* May 21 2022
*
* Comments/Reflections: The calculator uses B12Expression, MathPad, B12Button, and MathDisplay.
* The clock app uses Clock, Stopwatch, Timer, and TimeDisplay
*
* This project uses method relay code by Quark that has been modified
* - see https://forum.processing.org/two/discussion/13093/how-to-call-function-by-string-content.html for the source code
* This project uses the exp4j library unmodified, which is available under the Apache 2.0 license
* - see https://www.objecthunter.net/exp4j/index.html
*/
import net.objecthunter.exp4j.*; // Math expression evaluation library
// B12NumbersV3 // // B12NumbersV3 //
float scale = 2; String dbout = new String("");
float scale = 4;
PVector offset; PVector offset;
public static final int DECIMAL = 65; public static final int DECIMAL = 65;
MouseHandler mh; // Mouse event handler MouseHandler mh; // Mouse event handler
B12Expression ex; int mode; // State variable
Button modeButton;
ClockApp capp;
Calculator calc; Calculator calc;
Clock clock;
Button mode;
Button changeTime;
STime48 time;
void setup(){ void setup(){
size(400,400); // Initialize all the foundation stuff
size(800,800); //<>//
offset = new PVector(width/2, height/2); offset = new PVector(width/2, height/2);
time = new STime48(); scale = 4;
mh = new MouseHandler(new MouseData(offset, scale)); mh = new MouseHandler(new MouseData(offset, scale));
ex = new B12Expression(); offset = new PVector(width/2, height/2);
calc = new Calculator(mh, ex); mode = 0; // Set initial mode
mode = new Button(mh).setPos(new PVector(-20,-width/4), new PVector(40,20)).setRadius(2).setColor(#8B687F).autoHighlight().setText("Mode").setFunction(new MethodRelay(this, "changeMode")); // Create the apps
modeButton = new Button(mh).setPos(new PVector(-13,-offset.y / scale + 2), new PVector(26,10)).setRadius(2).setColor(#8B687F).autoHighlight().setText("Mode").setFunction(new MethodRelay(this, "changeMode"));
capp = new ClockApp(mh).setPos(0,-20);
calc = new Calculator(mh, new B12Expression());
} }
void draw(){ void draw(){
background(196); background(196);
mh.frameUpdate(offset, scale); mh.frameUpdate(offset, scale); // Updates scaled and offset mouse position data in mh.md (the MouseData object)
stroke(0);
strokeWeight(1);
//crossMark();
translate(offset.x,offset.y); translate(offset.x,offset.y);
scale(scale); scale(scale);
if(calc != null) calc.display(); modeButton.display();
if(clock != null) clock.display();
if(changeTime != null) changeTime.display();
mode.display(); switch(mode){
case 0: // Mode 0 is the clock app
capp.display(); break;
case 1: // Mode 1 is the calculator app
text("Calculator",-1,-offset.y/scale + 28);
calc.display(); break;
}
} }
// Mouse Handler code. This is used with buttons to run actions
void mouseClicked(){ void mouseClicked(){
// Every clickable element needs check whether the mouse is over it every frame, and if both clicked and mouseover then do action. // Every clickable element needs check whether the mouse is over it every frame, and if both clicked and mouseover then do action.
mh.cascade('c', mh.sMouseX(), mh.sMouseY(), mouseButton); mh.cascade('c', mh.sMouseX(), mh.sMouseY(), mouseButton);
@@ -50,29 +72,6 @@ void mouseReleased(){mh.cascade('r', mh.sMouseX(), mh.sMouseY(), mouseButton);}
void mouseWheel(MouseEvent event){mh.cascade('w', mh.sMouseX(), mh.sMouseY(), event.getCount());} void mouseWheel(MouseEvent event){mh.cascade('w', mh.sMouseX(), mh.sMouseY(), event.getCount());}
void mouseDragged(){mh.cascade('d', mh.sMouseX(), mh.sMouseY(), mouseButton);} void mouseDragged(){mh.cascade('d', mh.sMouseX(), mh.sMouseY(), mouseButton);}
void call(String _call){ void changeMode(){ // Changes state variable to switch apps
method(_call); mode = (mode + 1) % 2;
}
void changeMode(){
if(calc == null){
clock = null;
changeTime = null;
calc = new Calculator(mh, ex);
return;
}
calc = null;
clock = new Clock(time).setPos(new PVector(30,0));
changeTime = new Button(mh).setPos(new PVector(-40,-width/4 + 30), new PVector(80,20)).setRadius(2).setColor(#B096A7).autoHighlight().setText("Change Time").setFunction(new MethodRelay(clock, "setTime", Time48.class));
changeTime.setData(new Time48(12,0,0));
Runtime.getRuntime().gc();
}
void reset(){
calc.ex.clear();
}
void crossMark(){
line(offset.x,0,offset.x,height);
line(0,offset.y,width,offset.y);
} }

View File

@@ -1,70 +0,0 @@
/*
B12NumbersV3
Beta version of a clock in base 12.
by Nayan Sawyer
started Mar 2022
version 0.1.4.3 April 30 2022
Characters are a variation of Kaktovik Inupiaq numerals
reversed and in base 12 instead of 20. I take no credit
for the design.
Includes method relay code be Quark - see https://forum.processing.org/two/discussion/13093/how-to-call-function-by-string-content.html
for more details.
changelog 0.1.4.0
- Added MethodRelay code from Quark. Some fixes and
changes as well. Condesed some things into fewer files
for so the ide is easier to use, but they will be moved
back into their own files as version 1.0 approaches.
Everything is prep for adding gui elements (MethodRelay
included)
changelog 0.1.3
- Deprecated B12Char by rolling it's code into B12Digit.
Makes for easier to handle arrays, and will hopefully
make implementing the math functionality much easier.
It appears that only Clock may need true refactoring to
make the most of this change. B12Int and B12Float seem
to be fine with simply swithing out the reference.
*/
float scale = 2;
PVector offset;
float sMouseX;
float sMouseY;
public static final int DECIMAL = 65;
ClickHandler ch; // Mouse event handler
Calculator calc; ///blblblblb //<>//
void setup(){
size(400,400);
offset = new PVector(width/2, height/2);
ch = new ClickHandler();
}
void draw(){
background(196);
sMouseX = (mouseX - offset.x)/scale;
sMouseY = (mouseY - offset.y)/scale;
translate(offset.x,offset.y);
scale(scale);
point(0,0);
m.display();
clock.display();
//p.display();
t.display();
//println( + " " + ;
}
void mouseClicked(){
//clock.setTime(new Time48(16,0,0));
// Every clickable element needs check whether the mouse is over it every frame, and if both clicked and mouseover then do action.
ch.cascade(sMouseX, sMouseY);
}
void call(String _call){
method(_call);
}

View File

@@ -1,212 +0,0 @@
class Time48 extends Thread{
private int sec48;
private int min48;
private int hour48;
private int tsec48; // Total seconds
private boolean initialized;
// CONSTRUCTORS //
// TODO add throwing exceptions to all contructors
Time48(){
sec48 = 0;
min48 = 0;
hour48 = 0;
tsec48 = 0;
initialized = false;
}
Time48(int _tsec48){
tsec48 = _tsec48;
flattenTSec();
initialized = true;
}
Time48(Time48 t48){
tsec48 = t48.tsec();
flattenTSec();
initialized = true;
}
Time48(int h, int m, int s){
if(h >= 0 && h < 24){ hour48 = h;}else{throw new IllegalArgumentException();}
if(m >= 0 && m < 48){ min48 = m;}else{throw new IllegalArgumentException();}
if(s >= 0 && s < 48){ sec48 = s;}else{throw new IllegalArgumentException();}
flattenOther();
initialized = true;
}
// GETTERS //
int hours(){return hour48;}
int mins(){return min48;}
int secs(){return sec48;}
int tsec(){return tsec48;}
int[] t48(){int[] out = {hour48,min48,sec48}; return out;}
boolean initialized(){return initialized;}
int b10millis(){return int(float(tsec48) * 1562.5);}
Time48 copy(){ return new Time48(this); }
// SETTERS //
Time48 setHour(int h){
if(h < 0 || h >= 24) throw new IllegalArgumentException();
hour48 = h;
flattenOther();
initialized = true;
return this;
}
Time48 setMin(int m){
if(m < 0 || m >= 48) throw new IllegalArgumentException();
min48 = m;
flattenOther();
initialized = true;
return this;
}
Time48 setSec(int s){
if(s < 0 || s >= 48) throw new IllegalArgumentException();
sec48 = s;
flattenOther();
initialized = true;
return this;
}
Time48 setTsec(int s){ // TODO add exception
tsec48 = s;
flattenTSec();
initialized = true;
return this;
}
// PRIVATE FUNCTIONS //
private void flattenTSec(){
sec48 = tsec48;
min48 = sec48 / 48;
hour48 = min48 / 48;
sec48 -= min48 * 48;
min48 -= hour48 * 48;
}
private void flattenOther(){
tsec48 = hour48*48*48 + min48*48 + sec48;
}
// PLACEHOLDER FUNCTIONS //
public void run(){}
}
class STime48 extends Time48{
private int offset; // Time offset in milliseconds
private int tmillis; // Actual time at which time was synced to real world in milliseconds
private int syncedat; // Offset between the start of the program (when millis() starts counting from) and when the time was last synced
private boolean synced;
STime48(){
super();
offset = 0;
tmillis = 0;
syncedat = 0;
synced = false;
this.start();
}
// Public sync functions
public boolean synced(){return synced;}
public STime48 syncTime(){ synced = false; return this; } // Allows syncing after time starts running
public STime48 setTime(Time48 _time){
// To get offset we subtract where the current clock is from where we want it to be
offset = _time.b10millis() - millis() - tmillis + syncedat;
return this;
}
// Threaded code
@Override
public void run(){
while(true){
if(!synced){sync();}
delay(1); // MUST USE DELAY OR OFFSET DOES NOT GET CALCULATED
if(tmillis + millis() + offset > 86400000){ tmillis -= 86400000; } // Fall over at 00:00
setTsec(int((tmillis + millis() - syncedat + offset) / 1562.5)); // Add time at sync, millis since program start, and offset, and subtract the millis between program start and sync (because we're adding millis())
}
}
// Initial sync code
private void sync(){
int sec = second();
tmillis = 0;
while(true){
if(sec != second()){ // Wait until seconds changes so as to be as accurate as possible
tmillis = second()*1000 + minute()*60*1000 + hour()*60*60*1000; // Current time in total millis
syncedat = millis();
synced = true;
println("synced");
break;
}
}
}
}
class Clock {
PVector pos;
STime48 t48;
B12Int hours;
B12Int minutes;
B12Int seconds;
B12Digit sep; // TODO Just deprecated B12Char. Refactor to single array of B12Digits?
int tmillis;
Clock(STime48 _t48) {
pos = new PVector(0, 0);
t48 = _t48;
hours = new B12Int(t48.hours());
minutes = new B12Int(t48.mins());
seconds = new B12Int(t48.secs());
sep = new B12Digit(':'); // Seperation character between time columns
hours.setMinLen(2);
minutes.setMinLen(2); // Format all the ints to show a 0 in the 12s column if they are less than 12
seconds.setMinLen(2);
}
// GETTERS and SETTERS //
PVector getPos() { return pos; }
Clock setPos(PVector _pos) { pos = _pos.copy(); return this;}
Clock setPos(float _x, float _y) { pos = new PVector(_x, _y);return this;}
Clock setTime(Time48 _time) { t48.setTime(_time); return this;}
Clock resetTime() { t48.setTime(new Time48(0)); return this;}
void display() {
if (t48.synced()) {
// Time
hours.setValue(t48.hours());
minutes.setValue(t48.mins());
seconds.setValue(t48.secs());
// Position
hours.setPos(-64, 0);
minutes.setPos(-32, 0);
seconds.setPos(0, 0);
B12Digit c1 = new B12Digit(':');
B12Digit c2 = new B12Digit(':');
c1.setRefPos(-34, 0);
c2.setRefPos(-66, 0);
// Display
pushMatrix();
translate(pos.x, pos.y);
hours.display();
c2.display();
minutes.display();
c1.display();
seconds.display();
popMatrix();
} else {
text("fetching current time", pos.x, pos.y);
}
}
}

View File

@@ -55,8 +55,8 @@ class Button{
Button setDim(PVector _dim){dim = _dim.copy(); return this;} Button setDim(PVector _dim){dim = _dim.copy(); return this;}
Button setRect(PVector _pos, PVector _dim){pos = _pos; dim = _dim; return this;} Button setRect(PVector _pos, PVector _dim){pos = _pos; dim = _dim; return this;}
Button setRadius(float rad){radius = rad; return this;} Button setRadius(float rad){radius = rad; return this;}
Button setColor(color c){col = c; return this;} Button setColor(color c){colorMode(RGB,255); col = c; return this;}
Button setColor(color c, color h){col = c; highlight = h; return this;} Button setColor(color c, color h){colorMode(RGB,255); col = c; highlight = h; return this;}
Button autoHighlight(){ colorMode(RGB,255); highlight = color(int(red(col) * .85), int(green(col) * .85), int(blue(col) * .85)); return this; } Button autoHighlight(){ colorMode(RGB,255); highlight = color(int(red(col) * .85), int(green(col) * .85), int(blue(col) * .85)); return this; }
Button setHighlight(color h){ highlight = h; return this; } Button setHighlight(color h){ highlight = h; return this; }
Button setText(String t){text = t; return this;} Button setText(String t){text = t; return this;}
@@ -79,7 +79,7 @@ class Button{
fill(textColor); fill(textColor);
textSize(textSize); textSize(textSize);
if(renderPriority == 0){ if(renderPriority == 0){
while(textWidth(text) > dim.x * 0.95){ // WARNING! NOT ROBUST make this a function at some point to allow other rectModes to render properly while(textWidth(text) > dim.x * 0.95){ // WARNING! NOT ROBUST! make this a function at some point to allow other rectModes to render properly
textSize = textSize - 1; textSize = textSize - 1;
textSize(textSize); textSize(textSize);
} }
@@ -96,6 +96,7 @@ class Button{
if(mouseOver && mouseButton == LEFT){ if(mouseOver && mouseButton == LEFT){
//println("clicked" + this); //println("clicked" + this);
function.execute(data); function.execute(data);
mouseOver = false; // Very important. Without this a button retains its mouseOver status forever if it stops being displayed, causing it to register being clicked every time the mose is clicked anywhere
} }
} }

View File

@@ -1,4 +1,6 @@
class Calculator{ class Calculator{
/* Self contained Calculator widget. Can use an external expression object if data persistance is needed
*/
B12Expression ex; B12Expression ex;
MathPad m; MathPad m;
MathDisplay d; MathDisplay d;

108
B12NumbersV3/Clock.pde Normal file
View File

@@ -0,0 +1,108 @@
class Clock{
/*
Self contained clock widget with time setting functionality
*/
private PVector pos;
private MouseHandler mh;
private Button[] buttons;
private Button setTimeButton;
private boolean setTime;
private int cursorPos;
private STime48 time;
private TimeDisplay td;
Clock(MouseHandler _mh, STime48 _time){
pos = new PVector(0,0);
mh = _mh;
time = _time;
td = new TimeDisplay(time);
setTime = false;
cursorPos = 0;
initialize();
}
PVector getPos(){return pos;}
Clock setPos(PVector _pos){pos = _pos.copy(); initialize(); return this;}
Clock setPos(float x, float y){pos = new PVector(x,y); initialize(); return this;}
private void initialize(){
buttons = new Button[0];
td.setPos(pos.x + 13*4 + 2,pos.y-2);
// Create numpad buttons
for(int i = 0; i < 12; i++){
/* Button position must contain it's absolute position relative to sketch 0,0 for mouseOver to work.
This means we cannot translate and traw buttons, we mumst factor the parents position into the
absolute position of the button */
// x = pos.x + (width + gap) * (i%cols)
// y = pos.y + (height + gap) * b2rows - (height + gap) * row
PVector bPos = new PVector(pos.x + 22 * int(i%4) - 43,pos.y + 22 * 2 - 22 * floor(i/4) + 2);
buttons = (Button[])append(buttons, new B12Button(mh ,new B12Digit(i)).setPos(bPos).setDim(new PVector(20,20)).setFunction(new MethodRelay(this, "addChar", B12Digit.class)).setColor(220,150));
}
// Create other buttons
buttons = (Button[])append(buttons, new Button(mh).setText("Set").setPos(new PVector(pos.x - 43,pos.y + 22*3 + 2)).setDim(new PVector(27,20)).setFunction(new MethodRelay(this, "lockTime")).setColor(220,150));
buttons = (Button[])append(buttons, new Button(mh).setText("Clear").setPos(new PVector(pos.x + 29 - 43,pos.y + 22*3 + 2)).setDim(new PVector(28,20)).setFunction(new MethodRelay(this, "clearTime")).setColor(220,150));
buttons = (Button[])append(buttons, new Button(mh).setText("Cancel").setPos(new PVector(pos.x + 59 - 43,pos.y + 22*3 + 2)).setDim(new PVector(27,20)).setFunction(new MethodRelay(this, "cancelSetTime")).setColor(220,150));
setTimeButton = new Button(mh).setText("Set Time").setPos(new PVector(pos.x-21,pos.y + 2)).setDim(new PVector(42,13)).setFunction(new MethodRelay(this, "triggerSetTime")).setColor(220,150);
}
void addChar(B12Digit digit){
// Sets the time based on which button has been pressed, and where the cursor is
switch(cursorPos){
case 0:
td.setTime(new Time48().setHour(digit.getValue() * 12)); cursorPos += 1; break;
case 1:
td.setTime(new Time48(td.getTime().tsec()).setHour(digit.getValue() + td.getTime().hours())); cursorPos += 2; break;
case 3:
td.setTime(new Time48(td.getTime().tsec()).setMin(digit.getValue() * 12)); cursorPos += 1; break;
case 4:
td.setTime(new Time48(td.getTime().tsec()).setMin(digit.getValue() + td.getTime().mins())); cursorPos += 2; break;
case 6:
td.setTime(new Time48(td.getTime().tsec()).setSec(digit.getValue() * 12)); cursorPos += 1; break;
case 7:
td.setTime(new Time48(td.getTime().tsec() + digit.getValue())); cursorPos += 1; break;
}
}
// Sets the displayed time to a static 0
void clearTime(){
td.setTime(new Time48(0));
cursorPos = 0;
}
// Sets the synced time to that of the displayed time, then switches the diplayed time back to synced time
void lockTime(){
time.setTime(td.getTime());
td.setTime(time);
cursorPos = 0;
setTime = false;
}
// Switches mode to setting the time
void triggerSetTime(){
clearTime();
setTime = true;
}
// Cancels setting the time, reverting the displayed time back to what synced time was before
void cancelSetTime(){
td.setTime(time); //<>//
cursorPos = 0;
setTime = false;
}
void display(){
if(setTime){
for(int i = 0; i < buttons.length; i++){
buttons[i].display();
}
stroke(0);
if(cursorPos < 8)line(pos.x - 13 * (4-cursorPos) + 2, pos.y, pos.x - 13 * (4-cursorPos) + 10, pos.y);
}else{
setTimeButton.display();
}
td.display();
}
}

59
B12NumbersV3/ClockApp.pde Normal file
View File

@@ -0,0 +1,59 @@
class ClockApp{
/*
A container for positioning, displaying, and switching between a clock, stopwatch, and timer
*/
private PVector pos;
private MouseHandler mh;
private Clock clock;
private Stopwatch st;
private Timer timer;
private int mode;
private Button cmButton; // change mode button
ClockApp(MouseHandler _mh){
pos = new PVector(0,0);
mh = _mh; //<>//
initialize();
}
private void initialize(){
clock = new Clock(mh, new STime48()).setPos(pos);
st = new Stopwatch(mh).setPos(pos);
timer = new Timer(mh).setPos(pos);
mode = 0;
cmButton = new Button(mh).setPos(new PVector(-21,-mh.md.getOffset().y / scale + 18), new PVector(42,10)).setRadius(2).setColor(220,150).setText("Clock Mode").setFunction(new MethodRelay(this, "changeMode"));
}
private void resetPositions(){
clock.setPos(pos);
st.setPos(pos);
timer.setPos(pos);
}
PVector getPos(){return pos;}
ClockApp setPos(PVector _pos){pos = _pos.copy(); resetPositions(); return this;}
ClockApp setPos(float x, float y){setPos(new PVector(x,y)); return this;}
void display(){
cmButton.display();
fill(0);
textSize(10);
textAlign(CENTER,BOTTOM);
switch(mode){
case 0:
text("Clock",pos.x -2 ,pos.y-20);
clock.display(); break;
case 1:
text("Stopwatch",pos.x -2 ,pos.y-20);
st.display(); break;
case 2:
text("Timer",pos.x -2 ,pos.y-20);
timer.display(); break;
}
}
void changeMode(){
mode = (mode + 1) % 3;
}
}

View File

@@ -0,0 +1,22 @@
// LiveMethodRelay is simply a MethodRelay with tags and that can be "killed" so it will be removed from the MouseListener array
class LiveMethodRelay extends MethodRelay {
private boolean live;
private char tag;
LiveMethodRelay(Object obj, String name, char _tag, Class... args) {
super(obj, name, args);
tag = _tag;
live = true;
}
LiveMethodRelay(Object obj, String name, Class... args) {
super(obj, name, args);
tag = '\0';
live = true;
}
char getTag() {return tag;}
void setTag(char t) {tag = t;}
boolean live() {return live;}
void kill() {live = false;}
}

View File

@@ -1,4 +1,7 @@
class MathDisplay { class MathDisplay {
/*
Displays a B12Expression
*/
PVector pos; PVector pos;
B12Expression ex; B12Expression ex;
@@ -15,7 +18,7 @@ class MathDisplay {
translate(pos.x,pos.y); translate(pos.x,pos.y);
int count = 0; int count = 0;
for(int i = ex.length() - 1; i >= 0 ; i--){ for(int i = ex.length() - 1; i >= 0 ; i--){
ex.getDigit(i).setRefPos((-12 * (count+1)), 0); ex.getDigit(i).setPos((-12 * (count+1)), 0);
ex.getDigit(i).display(); ex.getDigit(i).display();
count++; count++;
} }

View File

@@ -1,4 +1,7 @@
class MathPad{ class MathPad{
/*
Creates and displays a button pad for the calculator input
*/
private B12Expression ex; private B12Expression ex;
private MouseHandler mh; private MouseHandler mh;
private Button[] buttons; private Button[] buttons;
@@ -21,7 +24,7 @@ class MathPad{
} }
void initialize(){ private void initialize(){
buttons = new Button[0]; buttons = new Button[0];
// Create numpad buttons // Create numpad buttons
for(int i = 0; i < 12; i++){ for(int i = 0; i < 12; i++){
@@ -41,7 +44,7 @@ class MathPad{
buttons = (Button[])append(buttons, new B12Button(mh, new B12Digit('*')).setPos(new PVector(pos.x + 22*4,pos.y)).setFunction(new MethodRelay(this, "addChar", B12Digit.class)).setColor(220,150)); buttons = (Button[])append(buttons, new B12Button(mh, new B12Digit('*')).setPos(new PVector(pos.x + 22*4,pos.y)).setFunction(new MethodRelay(this, "addChar", B12Digit.class)).setColor(220,150));
buttons = (Button[])append(buttons, new B12Button(mh, new B12Digit('/')).setPos(new PVector(pos.x + 22*4,pos.y + 22)).setFunction(new MethodRelay(this, "addChar", B12Digit.class)).setColor(220,150)); buttons = (Button[])append(buttons, new B12Button(mh, new B12Digit('/')).setPos(new PVector(pos.x + 22*4,pos.y + 22)).setFunction(new MethodRelay(this, "addChar", B12Digit.class)).setColor(220,150));
buttons = (Button[])append(buttons, new B12Button(mh, new B12Digit('.')).setPos(new PVector(pos.x + 22*4,pos.y + 22*2)).setFunction(new MethodRelay(this, "addChar", B12Digit.class)).setColor(220,150)); buttons = (Button[])append(buttons, new B12Button(mh, new B12Digit('.')).setPos(new PVector(pos.x + 22*4,pos.y + 22*2)).setFunction(new MethodRelay(this, "addChar", B12Digit.class)).setColor(220,150));
buttons = (Button[])append(buttons, new Button(mh).setText("Enter").setPos(new PVector(pos.x + 22*4,pos.y + 22*3)).setDim(new PVector(42,20)).setFunction(new MethodRelay(this.ex, "evaluate")).setColor(220,150)); //<>// buttons = (Button[])append(buttons, new Button(mh).setText("Enter").setPos(new PVector(pos.x + 22*4,pos.y + 22*3)).setDim(new PVector(42,20)).setFunction(new MethodRelay(this.ex, "evaluate")).setColor(220,150));
buttons = (Button[])append(buttons, new Button(mh).setText("Cl").setPos(new PVector(pos.x + 22*5,pos.y + 22)).setDim(new PVector(20,42)).setFunction(new MethodRelay(this.ex, "clear")).setColor(220,150)); buttons = (Button[])append(buttons, new Button(mh).setText("Cl").setPos(new PVector(pos.x + 22*5,pos.y + 22)).setDim(new PVector(20,42)).setFunction(new MethodRelay(this.ex, "clear")).setColor(220,150));
buttons = (Button[])append(buttons, new Button(mh).setText("Del").setPos(new PVector(pos.x + 22*5,pos.y)).setDim(new PVector(20,20)).setFunction(new MethodRelay(this.ex, "delete")).setColor(220,150)); buttons = (Button[])append(buttons, new Button(mh).setText("Del").setPos(new PVector(pos.x + 22*5,pos.y)).setDim(new PVector(20,20)).setFunction(new MethodRelay(this.ex, "delete")).setColor(220,150));
} }
@@ -56,36 +59,3 @@ class MathPad{
} }
} }
} }
class B12Button extends Button{
B12Digit digit;
B12Button(MouseHandler _mh, B12Digit _digit){
super(_mh);
digit = _digit;
setData(_digit);
}
// GETTERS AND SETTERS //
B12Digit getDigit(){ return digit; }
B12Button setDigit(B12Digit _digit){ digit = _digit; return this; }
// Add drawing the B12Digit to the display method
@Override
void display(){
super.display();
pushMatrix();
translate(super.pos.x,super.pos.y);
switch(super.mode){
case CORNER: digit.setRefPos(super.dim.x/2 - 4, super.dim.y - 2);
}
digit.display();
popMatrix();
}
}

View File

@@ -0,0 +1,77 @@
import java.lang.reflect.*;
import java.lang.ref.*;
/**
A class that encapsulates a named method to be invoked.
Quark 2015
see https://forum.processing.org/two/discussion/13093/how-to-call-function-by-string-content.html
Modified to use weak references. This is the only modification
*/
public static class MethodRelay {
/** The object to handle the draw event */
private WeakReference reference = null; // Replaced the original strong reference with a weak reference so that relays will not disable garbage collection for the object they reference
//private Object handlerObject = null;
/** The method in drawHandlerObject to execute */
private Method handlerMethod = null;
/** the name of the method to handle the event */
private String handlerMethodName;
/** An array of classes that represent the function
parameters in order */
private Class[] parameters = null;
/**
Register a method that has parameters.
parameter obj the object that contains the method to invoke
parameter name the name of the method
parameter args a comma separated list of
*/
MethodRelay(Object obj, String name, Class... args) {
try {
handlerMethodName = name;
parameters = args;
handlerMethod = obj.getClass().getMethod(handlerMethodName, parameters);
reference = new WeakReference(obj);
}
catch (Exception e) {
println("Unable to find the function -");
print(handlerMethodName + "( ");
if (parameters != null) {
for (Class c : parameters)
print(c.getSimpleName() + " ");
println(")");
}
}
}
/**
Register a method that has no parameters.
parameter obj the object that contains the method to invoke
parameter name the name of the method
*/
MethodRelay(Object obj, String name) {
this(obj, name, (Class[])null);
}
/**
Execute a paramerterless method
*/
void execute() {
execute((Object[])null);
}
/**
Execute a method with parameters
parameter data a comma separated list of values
to be passed to the method
*/
void execute(Object... data) {
if (reference.get() != null) {
try {
handlerMethod.invoke(reference.get(), data);
}
catch (Exception e) {
println("Error on invoke");
}
}
}
}

View File

@@ -0,0 +1,36 @@
class MouseData{
/* MouseData stores properly translated and scaled mouse position for things like buttons
*/
private PVector offset;
private float scale;
private float sMouseX;
private float sMouseY;
private float pSMouseX;
private float pSMouseY;
PVector getOffset(){return offset.copy();}
float getScale(){return scale;}
MouseData(PVector _offset, float _scale){
offset = _offset;
scale = _scale;
sMouseX = (mouseX - offset.x)/scale;
sMouseY = (mouseY - offset.y)/scale;
pSMouseX = (pmouseX - offset.x)/scale;
pSMouseY = (pmouseY - offset.y)/scale;
}
void update(PVector _offset, float _scale){
offset = _offset;
scale = _scale;
sMouseX = (mouseX - offset.x)/scale;
sMouseY = (mouseY - offset.y)/scale;
pSMouseX = (pmouseX - offset.x)/scale;
pSMouseY = (pmouseY - offset.y)/scale;
}
float sMouseX(){return sMouseX;}
float sMouseY(){return sMouseY;}
float pSMouseX(){return pSMouseX;}
float pSMouseY(){return pSMouseY;}
}

View File

@@ -0,0 +1,49 @@
class MouseHandler {
/* MouseHandler holds the code that listens for mouse actions and passes those
calls on to the individual LiveMethodRelay listeners in mrs that have been
sent to MouseHandler by buttons and other objects that want to listen for
mouse actions.
*/
private MouseData md;
private LiveMethodRelay[] mrs;
MouseHandler(MouseData _md) {
md = _md;
mrs = new LiveMethodRelay[0];
}
// PASSTHROUGH FOR MOUSE DATA //
float sMouseX(){return md.sMouseX();}
float sMouseY(){return md.sMouseY();}
float pSMouseX(){return md.pSMouseX();}
float pSMouseY(){return md.pSMouseY();}
void frameUpdate(PVector offset, float scale){md.update(offset, scale);} // Updates the position of the mouse so that it is translated and scaled properly
// This is used by outside objects to stick a listner in the listener array
void addRelay(LiveMethodRelay r) {
clean();
if(r.getTag() == '\0'){ throw new IllegalArgumentException("MouseHandler only accepts tagged LiveMethodRelays"); }
mrs = (LiveMethodRelay[])append(mrs, r);
}
// clean() removes any dead method relays
void clean() {
if (mrs.length == 0) return;
for (int i = mrs.length -1; i >= 0; i--) {
if (!mrs[i].live()) {
mrs[i] = mrs[mrs.length - 1];
mrs = (LiveMethodRelay[])shorten(mrs);
}
}
}
// Triggers all method relays marked with the input tag. This is called by the built in mouse function overrides
void cascade(char tag, float... data) {
clean();
for (int i = 0; i < mrs.length; i++) {
if(mrs[i].getTag() == tag){
mrs[i].execute(data);
}
}
}
}

7
B12NumbersV3/Number.pde Normal file
View File

@@ -0,0 +1,7 @@
abstract interface Number{
/* Interface allows storing B12Digits, B12Ints, and B12Floats using polymorphism
This is used in the calculator code when parsing the input
*/
abstract PVector getPos();
abstract void display();
}

53
B12NumbersV3/STime48.pde Normal file
View File

@@ -0,0 +1,53 @@
class STime48 extends Time48{
/* Synced time class which uses a seperate thread to update time
*/
private int offset; // Time offset in milliseconds. Used to set STime48 to something other than machine time
private int tmillis; // Actual time at which time was synced to real world in milliseconds.
private int syncedat; // Offset between the start of the program (when millis() starts counting from) and when the time was last synced
private boolean synced; // Stores STime48 status
STime48(){
super();
offset = 0;
tmillis = 0;
syncedat = 0;
synced = false;
this.start();
}
// Public sync functions
public boolean synced(){return synced;}
public STime48 syncTime(){ synced = false; return this; } // Allows re-syncing after time starts running
public STime48 setTime(Time48 _time){
// To get offset we subtract where the current clock is from where we want it to be
offset = _time.b10millis() - millis() - tmillis + syncedat;
return this;
}
// Threaded code
@Override
public void run(){
while(true){
if(!synced){sync();}
delay(1); // MUST USE DELAY OR OFFSET DOES NOT GET CALCULATED - probably caused by some annoying simultaneous memory access glitch
if(tmillis + millis() + offset > 86400000){ tmillis -= 86400000; } // Fall over at 00:00
setTsec(int((tmillis + millis() - syncedat + offset) / 1562.5)); // Add time at sync, millis since program start, and offset, and subtract the millis between program start and sync (because we're adding millis())
}
}
// Initial sync code
private void sync(){
int sec = second();
tmillis = 0;
while(true){
if(sec != second()){ // Wait until seconds changes so as to be as accurate as possible
tmillis = second()*1000 + minute()*60*1000 + hour()*60*60*1000; // Current time in total millis
syncedat = millis();
synced = true;
println("synced");
break;
}
}
}
}

View File

@@ -0,0 +1,59 @@
class Stopwatch{
/*
A self contained stopwatch widget
*/
private PVector pos;
private MouseHandler mh;
private STime48 time;
private TimeDisplay td;
private Button[] buttons;
private boolean running;
private color stopCol;
Stopwatch(MouseHandler _mh){
pos = new PVector(0,0);
mh = _mh;
time = new STime48().setTime(new Time48(0));
td = new TimeDisplay(new Time48(0));
buttons = new Button[0];
running = false;
initialize();
}
PVector getPos(){return pos;}
Stopwatch setPos(PVector _pos){pos = _pos.copy(); return this;}
Stopwatch setPos(float x, float y){pos = new PVector(x,y); return this;}
private void initialize(){
stopCol = 100;
td.setPos(pos.x + 13*4 + 2,pos.y-2).setCol(stopCol);
buttons = (Button[])append(buttons, new Button(mh).setText("Start").setPos(new PVector(pos.x - 14 - 30,pos.y + 2)).setDim(new PVector(28,16)).setFunction(new MethodRelay(this, "startt")).setColor(220,150));
buttons = (Button[])append(buttons, new Button(mh).setText("Stop").setPos(new PVector(pos.x - 14,pos.y + 2)).setDim(new PVector(28,16)).setFunction(new MethodRelay(this, "stopp")).setColor(220,150));
buttons = (Button[])append(buttons, new Button(mh).setText("Reset").setPos(new PVector(pos.x + 16,pos.y + 2)).setDim(new PVector(28,16)).setFunction(new MethodRelay(this, "reset")).setColor(220,150));
}
void display(){
for(int i = 0; i < buttons.length; i++){
buttons[i].display();
}
td.display();
}
void startt(){
if(running) return;
td.setTime(time.setTime(td.getTime())).setCol(0);
running = true;
}
void stopp(){
if(!running )return;
td.setTime(new Time48(time)).setCol(stopCol);
running = false;
}
void reset(){
td.setTime(new Time48(0)).setCol(stopCol);
running = false;
}
}

97
B12NumbersV3/Time48.pde Normal file
View File

@@ -0,0 +1,97 @@
class Time48 extends Thread{
/* A class which stores time staticly in base 12
*/
private int sec48;
private int min48;
private int hour48;
private int tsec48; // Total seconds
private boolean initialized;
// CONSTRUCTORS //
// TODO add throwing exceptions to all contructors
Time48(){
sec48 = 0;
min48 = 0;
hour48 = 0;
tsec48 = 0;
initialized = false;
}
Time48(int _tsec48){
tsec48 = _tsec48;
flattenTSec();
initialized = true;
}
Time48(Time48 t48){
tsec48 = t48.tsec();
flattenTSec();
initialized = true;
}
Time48(int h, int m, int s){
if(h >= 0 && h < 24){ hour48 = h;}else{throw new IllegalArgumentException();}
if(m >= 0 && m < 48){ min48 = m;}else{throw new IllegalArgumentException();}
if(s >= 0 && s < 48){ sec48 = s;}else{throw new IllegalArgumentException();}
flattenOther();
initialized = true;
}
// GETTERS //
int hours(){return hour48;}
int mins(){return min48;}
int secs(){return sec48;}
int tsec(){return tsec48;}
int[] t48(){int[] out = {hour48,min48,sec48}; return out;}
boolean initialized(){return initialized;}
int b10millis(){return int(float(tsec48) * 1562.5);}
Time48 copy(){ return new Time48(this); }
// SETTERS //
Time48 setHour(int h){
if(h < 0 || h >= 24) throw new IllegalArgumentException();
hour48 = h;
flattenOther();
initialized = true;
return this;
}
Time48 setMin(int m){
if(m < 0 || m >= 48) throw new IllegalArgumentException();
min48 = m;
flattenOther();
initialized = true;
return this;
}
Time48 setSec(int s){
if(s < 0 || s >= 48) throw new IllegalArgumentException();
sec48 = s;
flattenOther();
initialized = true;
return this;
}
Time48 setTsec(int s){ // TODO add exception
tsec48 = s;
flattenTSec();
initialized = true;
return this;
}
// PRIVATE FUNCTIONS //
// The "flatten" functions adjust the class attributes according to whichever attribute has been updated
private void flattenTSec(){
sec48 = tsec48;
min48 = sec48 / 48;
hour48 = min48 / 48;
sec48 -= min48 * 48;
min48 -= hour48 * 48;
}
private void flattenOther(){
tsec48 = hour48*48*48 + min48*48 + sec48;
}
// PLACEHOLDER FUNCTIONS //
public void run(){} // This is for STime48 to use threading
}

View File

@@ -0,0 +1,71 @@
class TimeDisplay {
/*
Displays a Time48. Can utilize both static Time48 and synced STime48
*/
private PVector pos;
private Time48 t48;
private B12Int hours;
private B12Int minutes;
private B12Int seconds;
private B12Digit[] digits;
//private int tmillis;
private color col;
TimeDisplay(Time48 _t48) {
pos = new PVector(0, 0);
t48 = _t48;
hours = new B12Int(t48.hours());
minutes = new B12Int(t48.mins());
seconds = new B12Int(t48.secs());
//sep = new B12Digit(':'); // Seperation character between time columns
hours.setMinLen(2);
minutes.setMinLen(2); // Format all the ints to show a 0 in the 12s column if they are less than 12
seconds.setMinLen(2);
}
// GETTERS and SETTERS //
PVector getPos() { return pos; }
Time48 getTime(){return t48;}
color getCol(){return col;}
TimeDisplay setPos(PVector _pos) { pos = _pos.copy(); return this;}
TimeDisplay setPos(float _x, float _y) { pos = new PVector(_x, _y);return this;}
TimeDisplay setCol(color _col){col = _col; return this;}
TimeDisplay setTime(Time48 _time) {
if(_time.getClass() == STime48.class){
t48 = _time;
return this;
}
t48 = _time.copy();
return this;
}
TimeDisplay resetTime(){ // TODO test this. does it work?
if(t48.getClass() != STime48.class){ println("Cannot reset a static time"); return this;}
t48 = ((STime48)t48).setTime(new Time48(0));
return this;
}
void display() {
digits = new B12Digit[0];
if(t48.getClass() == STime48.class){
while(!((STime48)t48).synced()){text("fetching current time", pos.x, pos.y);}
}
// Time
hours.setValue(t48.hours());
minutes.setValue(t48.mins());
seconds.setValue(t48.secs());
digits = (B12Digit[])concat(digits,seconds.getDigits());
digits = (B12Digit[])append(digits,new B12Digit(':'));
digits = (B12Digit[])concat(digits,minutes.getDigits());
digits = (B12Digit[])append(digits,new B12Digit(':'));
digits = (B12Digit[])concat(digits,hours.getDigits());
// Position
for(int i = 0; i < digits.length; i++){
digits[i].setPos(i*-13 + pos.x - 13,pos.y).setCol(col).display();
}
}
}

129
B12NumbersV3/Timer.pde Normal file
View File

@@ -0,0 +1,129 @@
class Timer{
/*
A self contained timer widget
Based on Clock, but different enough that it does not inherit it
Technically sets a synced time to zero, and subtracts whatever that
synced time currently happens to be from a target Time48 set to the
length of the timer and displays that as it gets smaller.
*/
PVector pos;
MouseHandler mh;
Button[] buttons;
Button setTimeButton;
boolean setTime;
int cursorPos;
STime48 time;
Time48 target;
TimeDisplay td;
boolean running;
Timer(MouseHandler _mh){
pos = new PVector(0,0);
mh = _mh;
time = new STime48().setTime(new Time48(0));;
target = new Time48(0);
td = new TimeDisplay(new Time48(0));
setTime = true;
running = false;
cursorPos = 0;
initialize();
}
PVector getPos(){return pos;}
Timer setPos(PVector _pos){pos = _pos.copy(); initialize(); return this;}
Timer setPos(float x, float y){pos = new PVector(x,y); initialize(); return this;}
private void initialize(){
buttons = new Button[0];
td.setPos(pos.x + 13*4 + 2,pos.y-2);
// Create numpad buttons
for(int i = 0; i < 12; i++){
/* Button position must contain it's absolute position relative to sketch 0,0 for mouseOver to work.
This means we cannot translate and traw buttons, we mumst factor the parents position into the
absolute position of the button */
// x = pos.x + (width + gap) * (i%cols)
// y = pos.y + (height + gap) * b2rows - (height + gap) * row
PVector bPos = new PVector(pos.x + 22 * int(i%4) - 43,pos.y + 22 * 2 - 22 * floor(i/4) + 2);
buttons = (Button[])append(buttons, new B12Button(mh ,new B12Digit(i)).setPos(bPos).setDim(new PVector(20,20)).setFunction(new MethodRelay(this, "addChar", B12Digit.class)).setColor(220,150));
}
// Create other buttons
buttons = (Button[])append(buttons, new Button(mh).setText("Start").setPos(new PVector(pos.x - 43,pos.y + 22*3 + 2)).setDim(new PVector(27,20)).setFunction(new MethodRelay(this, "lockTime")).setColor(220,150));
buttons = (Button[])append(buttons, new Button(mh).setText("Clear").setPos(new PVector(pos.x + 29 - 43,pos.y + 22*3 + 2)).setDim(new PVector(28,20)).setFunction(new MethodRelay(this, "clearTime")).setColor(220,150));
buttons = (Button[])append(buttons, new Button(mh).setText("Cancel").setPos(new PVector(pos.x + 59 - 43,pos.y + 22*3 + 2)).setDim(new PVector(27,20)).setFunction(new MethodRelay(this, "cancelSetTime")).setColor(220,150));
setTimeButton = new Button(mh).setText("Set Time").setPos(new PVector(pos.x-21,pos.y + 2)).setDim(new PVector(42,13)).setFunction(new MethodRelay(this, "triggerSetTime")).setColor(220,150);
}
void addChar(B12Digit digit){ // Same as Clock
switch(cursorPos){
case 0:
td.setTime(new Time48().setHour(digit.getValue() * 12)); cursorPos += 1; break;
case 1:
td.setTime(new Time48(td.getTime().tsec()).setHour(digit.getValue() + td.getTime().hours())); cursorPos += 2; break;
case 3:
td.setTime(new Time48(td.getTime().tsec()).setMin(digit.getValue() * 12)); cursorPos += 1; break;
case 4:
td.setTime(new Time48(td.getTime().tsec()).setMin(digit.getValue() + td.getTime().mins())); cursorPos += 2; break;
case 6:
td.setTime(new Time48(td.getTime().tsec()).setSec(digit.getValue() * 12)); cursorPos += 1; break;
case 7:
td.setTime(new Time48(td.getTime().tsec() + digit.getValue())); cursorPos += 1; break;
}
}
void clearTime(){
td.setTime(new Time48(0));
cursorPos = 0;
}
void lockTime(){
target = new Time48(td.getTime());
time.setTime(new Time48(0));
td.setTime(new Time48().setTsec(target.tsec() - time.tsec()));
cursorPos = 0;
setTime = false;
running = true;
}
void triggerSetTime(){
clearTime();
setTime = true;
running = false;
}
void cancelSetTime(){
//td.setTime(time);
cursorPos = 0;
setTime = false;
running = true;
}
void display(){
if(setTime){
for(int i = 0; i < buttons.length; i++){
buttons[i].display();
}
stroke(0);
if(cursorPos < 8)line(pos.x - 13 * (4-cursorPos) + 2, pos.y, pos.x - 13 * (4-cursorPos) + 10, pos.y);
}else{
setTimeButton.display();
}
if(running == true){ // This counts down when running, but allows displayed time to remain static when setting time
td.setTime(new Time48().setTsec(target.tsec() - time.tsec()));
}
td.display();
if(td.getTime().tsec() < 0){ // Display timer done message when timer is done. This also covers up whatever displayed time happens to be (weired negative signs atm)
colorMode(RGB,255);
fill(255,0,0);
rectMode(CORNERS);
rect(pos.x - 13*5,pos.y - 20,pos.x + 13*5,pos.y);
fill(0);
textSize(16);
textAlign(CENTER,BOTTOM);
text("Timer Done!",pos.x,pos.y);
}
}
}

View File

@@ -1,20 +1,73 @@
/* /*
B12NumbersV3 B12NumbersV3
Beta version of a clock in base 12. Beta version of a clock and calculator in base 12.
by Nayan Sawyer by Nayan Sawyer
started Mar 2022 started Mar 2022
version 0.1.5.7 April 30 2022 version 1.0.0.2 Oct 28 2022
Characters are a variation of Kaktovik Inupiaq numerals Characters are a variation of Kaktovik Inupiaq numerals,
reversed and in base 12 instead of 20. I take no credit the orientation of which has been flipped and in base 12
for the design. instead of 20. I take no credit for the design.
Includes method relay code be Quark - see https://forum.processing.org/two/discussion/13093/how-to-call-function-by-string-content.html Includes method relay code by Quark - see https://forum.processing.org/two/discussion/13093/how-to-call-function-by-string-content.html
for more details. for more details. Also includes the library exp4j for evaluating mathematical expressions.
// TODO add actual math evaluation to B12Expression // Once thiss is done we hit version 0.2.0.0 // // DONE put everything back into seperate tabs
// DONE fully document everything
// TODO add throwing exceptions to all contructors // TODO add throwing exceptions to all contructors
// MAYBE start clock widget structure // TODO add error message to calculator rather than having it do nothing when it doesn't work
// TODO add helper function to B12Expression.convert() so that conversion handles float/int determination on its own
// TODO refactor the base code to have ints and floats seperate from the digit and display code, and use a single object to convert a number to digits
// MAYBE add additional operations like power, log, and trig functions // MAYBE add additional operations like power, log, and trig functions
// MAYBE add other button highlight options like transparency
BUGTRACKER
- something fishy with button highlight colors
- lazy positioning with ClockApp. Should move Stopwatch too
- B12Float and B12Int return their digits in opposite orders
- B12Float still uses mode DECIMAL
- Time48 does not check its inputs in some constructors
- renderPriority in Button is only half implemented
- negative signs when timer ends (not urgent)
- B12Digit.setValue() does not check input and only accepts ints
- B12Digit.getValue() returns int not byte
changelog 1.0.0.2
- Added license and updated main B12NumbersV3 text header
changelog 1.0.0.1
- Some bug fixes, made more things private, and fixed
timer
changelog 1.0.0.0
- Finished project to fully working state. Still lots of
bugs to squash, and structural problems with the base
code of the gui elements. But I don't have time to perfect
all of it at the moment. I will probably come back to it
in the future.
Put everything back into seperate files
Finished documentation for final project submission
changelog 0.2.1.3
- Finished timer. All clock apps are now complete. Some
assembly required :P
changelog 0.2.1.2
- Finished Stopwatch
changelog 0.2.1.1
- Finished clock implementation
changelog 0.2.1.0
- Changes to the base code and the beginning of the clock
applications. File condensing (will be reversed)
changelog 0.2.0.0
- Evaluating expressions has been fully implemented using
exp4j. Various things have been added to the base classes
to support this.
changelog 0.1.5.8
- Tweaks for first beta release for class presentation
changelog 0.1.5.7 changelog 0.1.5.7
- Presentation display setup done. - Presentation display setup done.

29
LICENSE Normal file
View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2022, Nayan Sawyer
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,14 +1,19 @@
# B12NumbersBeta1 # B12NumbersV3
The characters are designed after the ingenious Kaktovik Iñupiaq numerals, which were created by a group of middle schoolers in Kaktovik Alaska, to represent the base 20 counting system in Iñupiaq, their Inuit language. Release version 1.0.0.1 now available! see [releases](https://github.com/GShadow5/B12NumbersBeta1/releases) for a download
I take no credit for the design of the characters. I have merely co-opted the design for the development of this project because it is elegant and convenient to use for compound base number systems, and looks nothing like the Arabic numeral system. Read the full write up on my blog [here](http://localhost:4321/blog/portfolio/b12numbersv3?referrer=github)!
The project is currently in the stage where I am building all of the component parts, so the code does not yet do much when run. Note - changes after 1.0.0.1 are source code text only, and do not impact the release version
The time system is now complete with robust syncing and offset features. The base 12 time system works slightly differently from our regular base 60 time. This is a basic clock and calculator app that represents all numbers using base 12. The characters are designed after the ingenious Kaktovik Iñupiaq numerals, which were created by a group of middle schoolers in Kaktovik Alaska, to represent the base 20 counting system in Iñupiaq, their Inuit language. I take no credit for the design of the characters. I have merely co-opted the design for the development of this project because it is elegant and convenient to use for compound base number systems, and looks nothing like the Arabic numeral system.
Hours are still 24, but because it's in base 12, 10 is noon. The current functionality includes a clock, stopwatch, and timer, all of which run slower than normal time, as days are broken into 24 hours/day, 48 minutes/hour, and 48 seconds/minute. Also included is a calculator for doing math operations in base 12.
Minutes: there are now 48 minutes in an hour
Seconds: there are now 48 seconds in a minute
The number system is such that horizontal ticks on top represent multiples of four, and vertical ticks represent multiples of one. Decimal 11 is 2 horizontal ticks, and 3 vertical ticks. However, since the project is in an alpha state, I will not go into great detail at this point. The number system is such that horizontal ticks on top represent multiples of four, and vertical ticks represent multiples of one. Decimal 11 is 2 horizontal ticks, and 3 vertical ticks. (8 + 3)
I had grander plans for this program, but once I got into the details of how to construct event handlers for the mouse, and discovered that singletons are not allowed in Processing, I decided to postpone this project indefinitely. I am currently learning Vulkan and C++, and I hope to do more UI stuff using that at some point in the future, so I will probably remake this project someday, but until then this is all there is.
## How to run
If you have Processing installed all you need to do is download the source code and open B12NumbersV3.pde in the Processing IDE
Alternatively, if you are on windows you can download the release version from the [releases](https://github.com/GShadow5/B12NumbersBeta1/releases) page, unzip it, and run the .exe file.