Refactor detail scene elements to single class

This commit is contained in:
Dennis Schoepf 2021-07-31 16:27:07 +02:00
parent 85214fad3f
commit d084511c6b
11 changed files with 162 additions and 335 deletions

View file

@ -4,19 +4,32 @@
"name": "block", "name": "block",
"path": "packages/block", "path": "packages/block",
"size": 2450, "size": 2450,
"contents": { "revealables": [
"contributors": [
{ {
"name": "Peter Dunne" "type": "LEGACY",
} "name": "trieNode",
], "path": "src/trieNode.ts",
"legacy": [], "contents": "This file is a bit long, you might want to take a clear look and refactor it. The contributors of this subpackage could help here. Try to reveal them to see their contact info.",
"packages": [ "url": "https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/trie/src/trieNode.ts",
"imageUrl": null
},
{ {
"name": "react" "type": "CONTRIBUTOR",
"name": "Holger Drewes (holgerd77)",
"path": null,
"contents": "This contributor has a lot of commits in this sub package, below you can find the contact information and a few commits from this repo.",
"url": "https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/trie/src/trieNode.ts",
"imageUrl": "https://avatars.githubusercontent.com/u/931137?v=4"
},
{
"type": "PACKAGE",
"name": "chalk",
"path": "https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/client/package.json",
"contents": "This package is used in this part of the project. Take a look at the documentation in order to make yourself familiar with it",
"url": "https://github.com/chalk/chalk",
"imageUrl": null
} }
] ]
} }
}
] ]
} }

View file

@ -1,19 +1,16 @@
import { BehaviorSubject, Subject } from 'rxjs'; import { BehaviorSubject, Subject } from 'rxjs';
import { mp5 } from '../main'; import { mp5 } from '../main';
import { Area } from './types';
export const playerHead$ = new Subject<{ x: number; y: number; w: number }>(); export const playerHead$ = new Subject<Area>();
export const revealedArea$ = new BehaviorSubject<{ x: number; y: number; w: number }>({ export const revealedArea$ = new BehaviorSubject<Area>({
x: 0, x: 0,
y: 0, y: 0,
w: 0, w: 0,
}); });
export function areasColliding( export function areasColliding(areaOne: Area, areaTwo: Area): boolean {
areaOne: { x: number; y: number; w: number },
areaTwo: { x: number; y: number; w: number },
log?: boolean
): boolean {
const distanceBetweenPoints = mp5.dist(areaOne.x, areaOne.y, areaTwo.x, areaTwo.y); const distanceBetweenPoints = mp5.dist(areaOne.x, areaOne.y, areaTwo.x, areaTwo.y);
const shapeArea = areaTwo.w / 2 + areaOne.w / 2; const shapeArea = areaTwo.w / 2 + areaOne.w / 2;

View file

@ -1,9 +1,10 @@
import { mp5 } from '../main'; import { mp5 } from '../main';
import { SCREEN_HEIGHT, SCREEN_WIDTH } from './constants/screen'; import { SCREEN_HEIGHT, SCREEN_WIDTH } from './constants/screen';
import { Edge } from './sketchObjects/Edge'; import { Edge } from './sketchObjects/Edge';
import { Coordinates, SubProject } from './types'; import { RevealableInterface, RevealableTypes } from './sketchObjects/Revealable';
import { Coordinates, JSONSubproject, SubProject } from './types';
export function getEdgeDimensions({ size }: SubProject): number { export function getEdgeDimensions({ size }: JSONSubproject): number {
const radius = size * 0.05; const radius = size * 0.05;
return radius > 150 ? 150 : radius; return radius > 150 ? 150 : radius;
} }
@ -41,7 +42,7 @@ export function generateEdgeCoords(existingEdges: Edge[]): Coordinates {
return newCoords; return newCoords;
} }
export function generateEdges(subprojects: SubProject[]): Edge[] { export function generateEdges(subprojects: JSONSubproject[]): Edge[] {
let edges = []; let edges = [];
subprojects.forEach((subproject) => { subprojects.forEach((subproject) => {
@ -65,6 +66,26 @@ export function generateEdges(subprojects: SubProject[]): Edge[] {
); );
} }
export function getSubproject(name: string, projects: SubProject[]): SubProject { export function getTypedSubproject(name: string, projects: JSONSubproject[]): SubProject {
return projects.filter((project) => project.name === name)[0]; return projects
.filter((project) => project.name === name)
.map((project) => ({
...project,
revealables: project.revealables.map((revealable) => ({
...revealable,
type: RevealableTypes[revealable.type],
})),
}))[0];
}
export function getRevealablesforSubproject(
subProjectName: string,
subProjects: JSONSubproject[]
): RevealableInterface[] {
return subProjects
.filter((subproject) => subproject.name === subProjectName)[0]
.revealables.map((revealable) => ({
...revealable,
type: RevealableTypes[revealable.type],
}));
} }

View file

@ -4,25 +4,22 @@ import { Contributor } from '../sketchObjects/Contributor';
import { Legacy } from '../sketchObjects/Legacy'; import { Legacy } from '../sketchObjects/Legacy';
import { Package } from '../sketchObjects/Package'; import { Package } from '../sketchObjects/Package';
import { Player } from '../sketchObjects/Player'; import { Player } from '../sketchObjects/Player';
import { Revealable, RevealableInterface } from '../sketchObjects/Revealable';
import store from '../store'; import store from '../store';
import { Scenes } from './scenes'; import { Scenes } from './scenes';
export class DetailScene { export class DetailScene {
player: Player; player: Player;
contributors: any[]; revealables: RevealableInterface[];
legacy: any[]; revealableObjects: Revealable[];
packages: any[];
constructor() { constructor() {
this.player = new Player(); this.player = new Player();
store.subscribe((state) => { store.subscribe((state) => {
this.contributors = state.currContributors.map( this.revealables = state.revealables;
(contributor) => new Contributor(100, 200, 50) this.revealableObjects = this.revealables.map(
); (revealable) => new Revealable(revealable, { x: 100, y: 200, w: 100 })
this.legacy = state.currLegacy.map((legacy) => new Legacy(200, 300, 100));
this.packages = state.currPackages.map(
(currPackage) => new Package(400, 300, 50, 'react', '<h3>Test</h3>')
); );
}); });
} }
@ -33,25 +30,18 @@ export class DetailScene {
this.player.drawOnReveal(); this.player.drawOnReveal();
this.player.follow(); this.player.follow();
this.contributors.forEach((contributor) => { this.revealableObjects.forEach((revObj) => {
contributor.draw(); revObj.draw();
});
this.legacy.forEach((legacyObj) => {
legacyObj.place();
});
this.packages.forEach((packageObj) => {
packageObj.draw();
}); });
this.player.move(); this.player.move();
} }
onSceneClick() { onSceneClick() {
// store.setState({ currentScene: Scenes.OVERVIEW });
this.player.reveal(); this.player.reveal();
this.packages.forEach((packageObj) => { this.revealableObjects.forEach((revObj) => {
packageObj.onClick(); revObj.onClick();
}); });
} }
} }

View file

@ -28,7 +28,7 @@ export class OverviewScene {
this.edges.forEach((edge, i) => { this.edges.forEach((edge, i) => {
const dist = mp5.dist(mp5.mouseX, mp5.mouseY, edge.x, edge.y); const dist = mp5.dist(mp5.mouseX, mp5.mouseY, edge.x, edge.y);
if (dist < edge.r) { if (dist < edge.r) {
store.getState().setDetailScene(edge.name); store.getState().setProjectMetadata(edge.name);
store.setState({ currentScene: Scenes.DETAIL }); store.setState({ currentScene: Scenes.DETAIL });
} }
}); });

View file

@ -1,100 +0,0 @@
import { combineLatest, map } from 'rxjs';
import { mp5 } from '../../main';
import { playerHead$, areasColliding, revealedArea$ } from '../area';
import { colors } from '../constants/colors';
import store from '../store';
enum ContribStates {
HIDDEN = 'HIDDEN',
REVEALED = 'REVEALED',
ACTIVE = 'ACTIVE',
INACTIVE = 'INACTIVE',
}
export class Contributor {
x: number;
y: number;
size: number;
currentSize: number;
startSize: number = 5;
name: string;
url: string;
messageContents: string;
wasTouched: boolean = false;
wasInteractedWith: boolean = false;
hover: boolean = false;
contribState: ContribStates = ContribStates.HIDDEN;
constructor(
x: number,
y: number,
size: number,
name?: string,
messageContents?: string,
packageURL?: string
) {
this.x = x;
this.y = y;
this.size = size;
this.name = name;
this.url = packageURL;
this.currentSize = this.startSize;
this.messageContents = messageContents;
combineLatest([revealedArea$, playerHead$]).subscribe(([revealedArea, playerHead]) => {
const isRevealed = areasColliding(
{ x: this.x, y: this.y, w: this.currentSize },
revealedArea
);
if (isRevealed) {
this.contribState = ContribStates.REVEALED;
} else {
this.contribState = ContribStates.HIDDEN;
}
// console.log(this.packageState);
});
}
public draw() {
mp5.noStroke();
mp5.rectMode('center');
mp5.ellipseMode('center');
mp5.angleMode('degrees');
if (this.contribState === ContribStates.HIDDEN) {
} else if (this.contribState === ContribStates.REVEALED) {
mp5.arc(this.x, this.y, this.size, this.size, 10, 170, mp5.CHORD);
mp5.fill(mp5.color(colors.redDark));
mp5.arc(this.x, this.y, this.size, this.size, 170, 370, mp5.CHORD);
mp5.fill(mp5.color(colors.greyDark));
mp5.ellipse(this.x - this.size / 5, this.y + this.size / 4, this.size / 5);
mp5.ellipse(this.x + this.size / 5, this.y + this.size / 4, this.size / 5);
mp5.arc(this.x, this.y + this.size / 3, this.size / 3, this.size / 5, 0, 180, mp5.CHORD);
} else if (this.contribState === ContribStates.ACTIVE) {
// Check if mouse is over the squares, if so animate and enable click
const mouseOverShape = areasColliding(
{ x: mp5.mouseX, y: mp5.mouseY, w: 70 },
{ x: this.x, y: this.y, w: this.currentSize }
);
if (mouseOverShape) {
this.hover = true;
} else {
this.hover = false;
}
} else if (this.contribState === ContribStates.INACTIVE) {
}
}
public onClick() {
if (this.hover && !this.wasInteractedWith) {
this.wasInteractedWith = true;
store.getState().addInfoMessage({
headline: this.name,
innerHTML: this.messageContents,
});
}
}
}

View file

@ -1,21 +0,0 @@
import { mp5 } from '../../main';
import { colors } from '../constants/colors';
export class Legacy {
x: number;
y: number;
size: number;
name: string;
revealed: boolean;
constructor(x: number, y: number, size: number, name?: string, profileURL?: string) {
this.x = x;
this.y = y;
this.size = size;
}
private place() {
mp5.fill(mp5.color(colors.red));
mp5.ellipse(this.x, this.y, this.size);
}
}

View file

@ -1,146 +0,0 @@
import { combineLatest, map } from 'rxjs';
import { mp5 } from '../../main';
import { playerHead$, areasColliding, revealedArea$ } from '../area';
import { colors } from '../constants/colors';
import store from '../store';
enum PackageStates {
HIDDEN = 'HIDDEN',
REVEALED = 'REVEALED',
ACTIVE = 'ACTIVE',
INACTIVE = 'INACTIVE',
}
export class Package {
x: number;
y: number;
size: number;
name: string;
url: string;
messageContents: string;
corners: number = 10;
startSize: number = 5;
currentSize: number;
wasTouched: boolean = false;
wasInteractedWith: boolean = false;
hover: boolean = false;
packageState: PackageStates = PackageStates.HIDDEN;
constructor(
x: number,
y: number,
size: number,
name?: string,
messageContents?: string,
packageURL?: string
) {
this.x = x;
this.y = y;
this.size = size;
this.name = name;
this.url = packageURL;
this.currentSize = this.startSize;
this.messageContents = messageContents;
combineLatest([revealedArea$, playerHead$]).subscribe(([revealedArea, playerHead]) => {
const isRevealed = areasColliding(
{ x: this.x, y: this.y, w: this.currentSize },
revealedArea
);
const isTouched = areasColliding({ x: this.x, y: this.y, w: this.size }, playerHead, true);
if (isRevealed) {
if (this.packageState !== PackageStates.ACTIVE) {
this.packageState = PackageStates.REVEALED;
}
if (this.wasInteractedWith) {
this.packageState = PackageStates.INACTIVE;
}
if (isTouched && !this.wasTouched) {
this.packageState = PackageStates.ACTIVE;
this.wasTouched = true;
}
} else {
if (this.wasInteractedWith) {
this.packageState = PackageStates.INACTIVE;
} else if (this.wasTouched && !this.wasInteractedWith) {
this.packageState = PackageStates.ACTIVE;
} else {
this.packageState = PackageStates.HIDDEN;
}
}
// console.log(this.packageState);
});
}
public draw() {
mp5.noStroke();
mp5.rectMode('center');
mp5.ellipseMode('center');
if (this.packageState === PackageStates.HIDDEN) {
// Scale down after it was revealed or active
if (this.currentSize > this.startSize) {
this.currentSize--;
}
/*mp5.square(this.x, this.y, this.currentSize, this.corners);*/
} else if (this.packageState === PackageStates.REVEALED) {
// Scale up if not large enough
if (this.currentSize < this.size) {
this.currentSize++;
}
mp5.square(this.x, this.y, this.currentSize, this.corners);
mp5.square(this.x - this.size - 10, this.y, this.currentSize, this.corners);
mp5.square(this.x, this.y - this.size - 10, this.currentSize, this.corners);
} else if (this.packageState === PackageStates.ACTIVE) {
mp5.fill(mp5.color(colors.redLight));
mp5.square(this.x, this.y, this.currentSize, this.corners);
mp5.square(this.x - this.size - 10, this.y, this.currentSize, this.corners);
mp5.square(this.x, this.y - this.size - 10, this.currentSize, this.corners);
// Check if mouse is over the squares, if so animate and enable click
const mouseOverShape = areasColliding(
{ x: mp5.mouseX, y: mp5.mouseY, w: 70 },
{ x: this.x, y: this.y, w: this.currentSize }
);
if (mouseOverShape) {
this.hover = true;
mp5.square(this.x, this.y, this.currentSize + mp5.random(-5, 5), this.corners);
mp5.square(
this.x - this.size - 10,
this.y,
this.currentSize + mp5.random(-5, 5),
this.corners
);
mp5.square(
this.x,
this.y - this.size - 10,
this.currentSize + mp5.random(-5, 5),
this.corners
);
} else {
this.hover = false;
}
} else if (this.packageState === PackageStates.INACTIVE) {
mp5.fill(mp5.color(colors.greyLight));
mp5.square(this.x, this.y, this.currentSize, this.corners);
mp5.square(this.x - this.size - 10, this.y, this.currentSize, this.corners);
mp5.square(this.x, this.y - this.size - 10, this.currentSize, this.corners);
}
}
public onClick() {
if (this.hover && !this.wasInteractedWith) {
this.wasInteractedWith = true;
store.getState().addInfoMessage({
headline: this.name,
innerHTML: this.messageContents,
});
}
}
}

View file

@ -0,0 +1,63 @@
import { combineLatest } from 'rxjs';
import { mp5 } from '../../main';
import { areasColliding, playerHead$, revealedArea$ } from '../area';
import { colors } from '../constants/colors';
import { Area } from '../types';
export enum RevealableTypes {
LEGACY = 'LEGACY',
CONTRIBUTOR = 'CONTRIBUTOR',
PACKAGE = 'PACKAGE',
}
export interface RevealableInterface {
type: RevealableTypes;
name: string;
contents: string;
url: string;
path?: string;
imageUrl?: string;
}
enum RevealableStates {
HIDDEN = 'HIDDEN',
REVEALED = 'REVEALED',
FOUND = 'FOUND',
}
export class Revealable {
state: RevealableStates = RevealableStates.HIDDEN;
area: Area;
hover: boolean;
constructor({ type, name, path, contents, url, imageUrl }: RevealableInterface, area: Area) {
this.area = area;
combineLatest([revealedArea$, playerHead$]).subscribe(([revealedArea, playerHead]) => {
const isRevealed = areasColliding(revealedArea, this.area);
const isHovered = areasColliding(playerHead, this.area);
if (isRevealed && isHovered) {
this.state = RevealableStates.FOUND;
} else if (isRevealed && !isHovered) {
this.state = RevealableStates.REVEALED;
} else {
this.state = RevealableStates.HIDDEN;
}
});
}
public draw() {
if (this.state === RevealableStates.HIDDEN) {
mp5.fill(mp5.color(colors.greyLight));
} else if (this.state === RevealableStates.REVEALED) {
mp5.fill(mp5.color(colors.red));
} else if (this.state === RevealableStates.FOUND) {
mp5.fill(mp5.color(colors.redDark));
}
mp5.ellipse(this.area.x, this.area.y, this.area.w);
}
public onClick() {}
}

View file

@ -3,9 +3,10 @@ import { devtools } from 'zustand/middleware';
import { Scenes } from './scenes/scenes'; import { Scenes } from './scenes/scenes';
import { CompanionMessage, CompanionState } from './ui/companion'; import { CompanionMessage, CompanionState } from './ui/companion';
import project from '../metadata/project.json'; import project from '../metadata/project.json';
import { getSubproject } from './helpers';
import { SubProject } from './types';
import { InfoMessageType } from './ui/info'; import { InfoMessageType } from './ui/info';
import { RevealableInterface, RevealableTypes } from './sketchObjects/Revealable';
import { SubProject } from './types';
import { getRevealablesforSubproject } from './helpers';
export interface State { export interface State {
currentScene: Scenes; currentScene: Scenes;
@ -15,10 +16,8 @@ export interface State {
addInfoMessage: (newMessage: InfoMessageType) => void; addInfoMessage: (newMessage: InfoMessageType) => void;
userMessages: CompanionMessage[]; userMessages: CompanionMessage[];
addUserMessage: (newMessage: CompanionMessage) => void; addUserMessage: (newMessage: CompanionMessage) => void;
currContributors: any; revealables: RevealableInterface[];
currLegacy: any; setProjectMetadata: (projectName: string) => void;
currPackages: any;
setDetailScene: (packageName: string) => void;
} }
const store = create<State>( const store = create<State>(
@ -32,17 +31,11 @@ const store = create<State>(
userMessages: [], userMessages: [],
addUserMessage: (newMessage) => addUserMessage: (newMessage) =>
set((state) => ({ userMessages: [...state.userMessages, newMessage] })), set((state) => ({ userMessages: [...state.userMessages, newMessage] })),
currContributors: [], revealables: [],
currLegacy: [], setProjectMetadata: (projectName) =>
currPackages: [], set({
setDetailScene: (packageName) => revealables: getRevealablesforSubproject(projectName, project.subprojects),
set(() => ({ }),
currContributors: getSubproject(packageName, project.subprojects as SubProject[]).contents
.contributors,
currLegacy: getSubproject(packageName, project.subprojects as SubProject[]).contents.legacy,
currPackages: getSubproject(packageName, project.subprojects as SubProject[]).contents
.packages,
})),
})) }))
); );

View file

@ -1,15 +1,32 @@
import { RevealableInterface } from './sketchObjects/Revealable';
export interface SubProject { export interface SubProject {
name: string; name: string;
path: string; path: string;
size: number; size: number;
contents: { revealables: RevealableInterface[];
contributors: any[];
legacy: any[];
packages: any[];
};
} }
export interface JSONSubproject {
name: string;
path: string;
size: number;
revealables: Array<{
type: string;
name: string;
contents: string;
url: string;
path?: string;
imageUrl?: string;
}>;
}
export interface Coordinates { export interface Coordinates {
x: number; x: number;
y: number; y: number;
} }
export interface Area {
x: number;
y: number;
w: number;
}