xxxxxxxxxx
656
// this project uses the p5.js library
// the library can be found at https://p5js.org
// this project uses the I_AM_UI (aka. fez-ui on github) library for p5.js
// the library can be found at https://github.com/zturtledog/I_AM_UI/blob/main/I_AM_UI.js
function setup() {
createCanvas(800, 650);
angleMode(DEGREES)
}
let debug = true; // enables rendering of debug lines
let DTcount = 0;
let timescale = 0
function deepCopy(array) {
return JSON.parse(JSON.stringify(array));
}
let joints = []; // [0 = name, 1 = rotation, 2 = X, 3 = Y]
function jointOffset(name, X, Y) {
let joint;
for (let i = 0; i < joints.length; i++) {
if (joints[i][0] == name) {
joint = joints[i];
}
}
if (joint == null) return;
let Jr = joint[1];
let Jx = joint[2];
let Jy = joint[3];
let x = cos(Jr) * Y + cos(Jr + 180 / 2) * X + Jx;
let y = sin(Jr) * Y + sin(Jr + 180 / 2) * X + Jy;
return [x, y];
}
function createJoint(name, R, X, Y) {
joints[joints.length] = [name, R, X, Y];
}
function setJoint(name, R, X, Y) {
for (let i = 0; i < joints.length; i++) {
if (joints[i][0] == name) {
joints[i] = [name, R, X, Y];
}
}
}
function linkJoint(name, linkName, R, L) {
let joint;
for (let i = 0; i < joints.length; i++) {
if (joints[i][0] == name) {
joint = i;
}
}
if (joint == null) {
print("no joint");
return;
}
let link;
for (let i = 0; i < joints.length; i++) {
if (joints[i][0] == linkName) {
link = i;
}
}
if (link == null) {
print("no link");
return;
}
let r = joints[link][1] + R;
let x = cos(r) * L + joints[link][2];
let y = sin(r) * L + joints[link][3];
joints[joint] = [name, r, x, y];
if (debug) {
stroke(255)
line(joints[link][2], joints[link][3], joints[joint][2], joints[joint][3]);
}
}
function pointsLine(POINT1, POINT2) {
line(POINT1[0], POINT1[1], POINT2[0], POINT2[1]);
}
function pointsTriangle(POINT1,POINT2,POINT3){
triangle(POINT1[0], POINT1[1], POINT2[0], POINT2[1], POINT3[0], POINT3[1])
}
// the evil plans \/
/*
groups contains all of the user created stuff
formats:
{ type = "GROUP", name, x, y, contents = [], enabled }
groups should be used like layers, content cannot be accesed by other groups
{ type = "JOINT", id, rotation, X, Y }
id will only be used by the system to differentiate joints
{ type = "JOINTLINK", Bjid, Ljid, R, L }
links two joints, R and L define the rotation and length offsets for the linked joint
{ "POINT", id, joint, x, y}
a point connected to a joint
{ "LINE", id, p1, p2, thickness, r, g, b}
creates a line between points
{ "TRIANGLE", id, p1, p2, p3, r, g, b }
creates a triangle to fill the space between three points
ids should be formatted like "{GROUPNAME} {TYPE} {NUMBER}"
with spaces so I can parse them
*/
// to test we're gonna just manually enter stuff
groups = [ {type: "GROUP", name:"default", x:0, y:0, contents:[
{type: "JOINT", id:"", R:0, x:150, y:100},
{type: "JOINT", id:"", R:0, x:150, y:150},
{type: "JOINT", id:"", R:0, x:150, y:150},
{type: "JOINTLINK", id:"", Bjid:"default JOINT 1", Ljid:"default JOINT 2", R:0, L:50},
{type: "JOINTLINK", id:"", Bjid:"default JOINT 0", Ljid:"default JOINT 1", R:0, L:50},
{type: "POINT", id:"", joint: "default JOINT 0", x: 0, y: 10},
{type: "POINT", id:"", joint: "default JOINT 0", x: 10, y: -10},
{type: "POINT", id:"", joint: "default JOINT 0", x: -10, y: -10},
{type: "LINE", id:"", p1: "default POINT 0", p2: "default POINT 1", thickness: 1, r:0, g:0, b:255},
{type: "LINE", id:"", p1: "default POINT 1", p2: "default POINT 2", thickness: 1, r:0, g:0, b:255},
{type: "LINE", id:"", p1: "default POINT 2", p2: "default POINT 0", thickness: 1, r:0, g:0, b:255},
{type: "TRIANGLE", id:"", p1:"default POINT 0", p2:"default POINT 1", p3:"default POINT 2", r:0, g:255, b:255}
], enabled:true, order: 2},
{type: "GROUP", name: "test", x:0, y:0, contents: [
], enabled:true, order: 1}]
/*
animations: functions to move change values over time, only affects objects
{type = "TIME", id, val, mult, offset}
sets the target value to time * multiplier + offset
not recommended to be used on values other than rotation since this goes up (or down) forever
{type = "SINE", id, val, amplitude, frequency, phase, offset}
oscilates the target value, uses the formula:
sin( (time + phase) * 360 * frequency ) * amplitude + offset
(the *360 makes it so that the default frequency is one ocilation a second, so frequency = oscilations/second)
val can be any part of a component that is a number, including color
thats all imma add for now, keyframes later, those will be more complicated
*/
animations = [
{type:"TIME", id:"default JOINTLINK 0", val:"R", mult:180, offset:0},
{type:"SINE", id:"default JOINTLINK 1", val:"R", amplitude: 30, frequency: 0.5, phase: 0, offset: 0}
]
function objectFromid(id){
// break up the id into its three parts
idParts = split(id, " ")
// search for the specified group
for(let g = 0; g < groups.length; g++){
// check if the group has the matching name
if(groups[g].name == idParts[0]){
for(let i = 0; i < groups[g].contents.length; i++){
// find the matching object
if(groups[g].contents[i].id == id){
return groups[g].contents[i]
}
}
}
}
}
let editorVisible = true
let editorScreen = "groups"
let theTextInputTM = new onelinetext("The Text/Number Input™",530,50)
/*
should be formatted as either:
"groupname value" or
"groupname objecttype index value"
used to determine which value is being edited
*/
let selectedInput = null
let selectedObject = null
let selectedGroup = null
// should be either "joint" or "point" (heheh that rhymes)
let selectionType = "joint"
// pass the value of theTextInputTM into the selected parameter
function passTextInput(){
let info = split(selectedInput, " ")
// value of a group selected
if(info.length == 2){
let group
for(let g = 0; g < groups.length; g++){
if(groups[g].name == info[0]) group = groups[g]
}
if(group == null) return
if(info[1] == "name"){
let identical = false
for(let g = 0; g < groups.length; g++){
if(groups[g].name == theTextInputTM.text) identical = true
}
if(!identical){
group.name = theTextInputTM.text
}
} else {
let num = Number(theTextInputTM.text)
if(!isNaN(num)){
if(info[1] == "order"){
let identical = false
for(let g = 0; g < groups.length; g++){
if(groups[g].order == num) identical = true
}
if(!identical){
group.order = num
}
} else{
group[info[1]] = num
}
}
}
}
// value of an object selected
if(info.length == 4){
let num = Number(theTextInputTM.text)
if(!isNaN(num)){
let object = objectFromid(info[0] + " " + info[1] + " " + info[2])
object[info[3]] = num
}
}
theTextInputTM.text = ""
selectedInput = null
}
function buttonWithText(x,y,w,h,tex,col,padding){
let ret = button(x,y,w,h)
textSize(h - (padding||2 * 2))
textAlign(CENTER,CENTER)
fill(col||255)
noStroke()
text(tex,x,y,w,h)
return ret
}
function keyPressed(){
if(keyCode == ENTER){
if(selectedInput != null) passTextInput()
}
if( keyCode == 32 ){
if(editorVisible){
editorVisible = false
timescale = 1
} else {
editorVisible = true
timescale = 0
DTcount = 0
}
}
}
function draw() {
background(10);
strokeWeight(1.5);
stroke(255);
fill(0);
DTcount += (deltaTime / 1000) * timescale;
// the system that intetrprets the things
for(let g = 0; g < groups.length; g++){
let group = groups[g]
// give any object that doesn't have an id an id
// go through each group's contents
for(let i = 0; i < group.contents.length; i++){
// define the content that is currently being checked as a variable because I don't want to have to keep retyping that spagetti
let content = group.contents[i]
// check if the object doesn't have an id
if(content.id == ""){
// get the group name
let Gname = group.name
// get the object type
let Otype = content.type
// go through each object in the group, for each object that has an id, raise the counter, this will be the number part of the id\
let num = 0
for(let n = 0; n < group.contents.length; n++){
if(group.contents[n].type == Otype && group.contents[n].id != ""){
num++
}
}
// assign the id
content.id = Gname + " " + Otype + " " + num
print(content.id)
}
}
// have animations set the values of the objects
for(let a = 0; a < animations.length; a++){
let object
let animation
switch(animations[a].type){
case "TIME":
// find the connected object
object = objectFromid(animations[a].id)
// set the object's value
object[ animations[a].val ] = DTcount * animations[a].mult + animations[a].offset
break
case "SINE":
// find the connected object
object = objectFromid(animations[a].id)
animation = animations[a]
// set the object's value
object[ animations[a].val ] = sin( (DTcount + animation.phase) * 360 * animation.frequency ) * animation.amplitude + animation.offset
break;
}
}
}
// sort the groups based on order
// I don't think this works lol
let lowest = -99999999999999999
let currentLowest = lowest
let order = []
for(let i = 0; i < groups.length; i++){
let index = 0
currentLowest = 99999999999999
for(let g = 0; g < groups.length; g++){
let group = groups[g]
if(group.order < currentLowest && group.order > lowest){
currentLowest = group.order
index = g
}
}
order[order.length] = index
lowest = currentLowest
}
// run through the groups in order, and check if they're enabled
for(let g = 0; g < order.length; g++){
let group = groups[order[g]]
if(group.enabled){
// clear joints
joints = []
// create joints
for(let i = 0; i < group.contents.length; i++){
let content = group.contents[i]
if(content.type == "JOINT"){
createJoint(
content.id,
content.R,
content.x,
content.y
)
}
}
// link joints
// this need to run recursively so that several joints connect properly
// either I make an algorithm to determine the order or I just square the loop
// ngl there shouldn't be too many joints per group so it should be fine
// how many times the recursion will run
let linkCount = 0
for(let l = 0; l < group.contents.length; l++){
if(group.contents[l].type == "JOINTLINK") linkCount++
}
for(let r = 0; r < linkCount; r++){
// link the joints according to JOINTLINK objects
for(let i = 0; i < group.contents.length; i++){
let object = group.contents[i]
if(object.type == "JOINTLINK"){
linkJoint(
object.Bjid,
object.Ljid,
object.R,
object.L
)
}
}
}
// create points
let points = []
for(let i = 0; i < group.contents.length; i++){
let object = group.contents[i]
if(object.type == "POINT"){
let coords = jointOffset(object.joint, object.x, object.y)
if(debug){
stroke(0,255,255)
strokeWeight(1)
point(coords[0], coords[1])
}
points[points.length] = {id:object.id, x:coords[0], y:coords[1]}
}
}
// draw triangles
for(let i = 0; i < group.contents.length; i++){
let object = group.contents[i]
if(object.type == "TRIANGLE"){
let point1 = null
for(let p = 0; p < points.length; p++){
if(points[p].id == object.p1) point1 = p
}
let point2 = null
for(let p = 0; p < points.length; p++){
if(points[p].id == object.p2) point2 = p
}
let point3 = null
for(let p = 0; p < points.length; p++){
if(points[p].id == object.p3) point3 = p
}
if( point1 != null && point2 != null && point3 != null){
fill(object.r, object.g, object.b)
noStroke()
triangle(
points[point1].x,
points[point1].y,
points[point2].x,
points[point2].y,
points[point3].x,
points[point3].y
)
}
}
}
// draw lines
for(let i = 0; i < group.contents.length; i++){
let object = group.contents[i]
if(object.type == "LINE"){
// get the index of the points
let point1 = null
for(let p = 0; p < points.length; p++){
if(points[p].id == object.p1) point1 = p
}
let point2 = null
for(let p = 0; p < points.length; p++){
if(points[p].id == object.p2) point2 = p
}
// if both points could be found, draw a line using the object's parameters
if( point1 != null && point2 != null){
strokeWeight(object.thickness)
stroke(object.r, object.g, object.b)
line(
points[point1].x,
points[point1].y,
points[point2].x,
points[point2].y
)
}
}
}
}
}
// THE EDITOR (DUM DUM DUUUUUUM)
if(editorVisible){
fill(10)
stroke(0,0,255)
strokeWeight(2)
rect(510,10,280,630)
fill(220)
if(selectedInput == null){
stroke(120)
} else {
stroke(0,255,255)
}
rect(520,20,260,40)
fill(0)
noStroke()
theTextInputTM.update()
textAlign(LEFT,BOTTOM)
theTextInputTM.draw()
if(selectedGroup == null){
// create each group button
for(let g = 0; g < groups.length; g++){
let group = groups[order[g]]
fill(10)
stroke(0,0,255)
strokeWeight(2)
rect(520,80 + (g * 40),200,30)
if(selectedInput == group.name + " name"){
stroke(0,255,0)
} else {
stroke(0,0,255)
}
if(buttonWithText(523, 83 + (g * 40), 38, 24, "order",[0,0,255],10)){
selectedInput = group.name + " order"
}
stroke(255,0,0)
fill(10)
if(buttonWithText(565, 83 + (g * 40), 48, 24, "delete",[255,0,0],10)){
}
fill(0,0,255)
textAlign(LEFT,CENTER)
noStroke()
textSize(14)
text(group.name, 617, 84 + (g * 40), 118, 24)
text(group.order, 725, 84 + (g * 40), 20, 24)
}
}
uiupd()
}
}