Skip to content

Commit

Permalink
Merge pull request #49 from odama626/multiselect
Browse files Browse the repository at this point in the history
Multiselect
  • Loading branch information
odama626 authored Dec 20, 2024
2 parents ed94574 + 592b97e commit 39e4808
Show file tree
Hide file tree
Showing 21 changed files with 758 additions and 275 deletions.
Binary file added public/loading-texture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,21 @@ body {
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}

.selectBox {
border: 1px solid #55aaff;
background-color: rgba(75, 160, 255, 0.3);
position: fixed;
}

.text-shadow {
text-shadow:
0px 0px 5px black,
0px 0px 5px black,
0px 0px 5px black,
0px 0px 5px black,
0px 0px 5px black,
0px 0px 5px black,
0px 0px 5px black,
0px 0px 5px black;
}
95 changes: 95 additions & 0 deletions src/lib/SelectionHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { Vector2, WebGLRenderer } from 'three';

class SelectionHelper {
private element;
private startPoint;
private pointTopLeft;
private pointBottomRight;
public isDown;
public enabled;
public onPointerDown;
onPointerMove: (event: any) => void;
onPointerUp: () => void;

constructor(private renderer: WebGLRenderer, cssClassName: string) {
this.element = document.createElement('div');
this.element.classList.add(cssClassName);
this.element.style.pointerEvents = 'none';

this.renderer = renderer;

this.startPoint = new Vector2();
this.pointTopLeft = new Vector2();
this.pointBottomRight = new Vector2();

this.isDown = false;
this.enabled = true;

this.onPointerDown = function (event) {
if (this.enabled === false) return;

this.isDown = true;
this.onSelectStart(event);
}.bind(this);

this.onPointerMove = function (event) {
if (this.enabled === false) return;

if (this.isDown) {
this.onSelectMove(event);
}
}.bind(this);

this.onPointerUp = function () {
if (this.enabled === false) return;

this.isDown = false;
this.onSelectOver();
}.bind(this);

// this.renderer.domElement.addEventListener('pointerdown', this.onPointerDown);
// this.renderer.domElement.addEventListener('pointermove', this.onPointerMove);
// this.renderer.domElement.addEventListener('pointerup', this.onPointerUp);
}

dispose() {
// this.renderer.domElement.removeEventListener('pointerdown', this.onPointerDown);
// this.renderer.domElement.removeEventListener('pointermove', this.onPointerMove);
// this.renderer.domElement.removeEventListener('pointerup', this.onPointerUp);
}

onSelectStart(event) {
this.element.style.display = 'none';
this.renderer.domElement.parentElement.appendChild(this.element);

this.element.style.left = event.clientX + 'px';
this.element.style.top = event.clientY + 'px';
this.element.style.width = '0px';
this.element.style.height = '0px';

this.startPoint.x = event.clientX;
this.startPoint.y = event.clientY;
}

onSelectMove(event) {
this.element.style.display = 'block';

this.pointBottomRight.x = Math.max(this.startPoint.x, event.clientX);
this.pointBottomRight.y = Math.max(this.startPoint.y, event.clientY);
this.pointTopLeft.x = Math.min(this.startPoint.x, event.clientX);
this.pointTopLeft.y = Math.min(this.startPoint.y, event.clientY);

this.element.style.left = this.pointTopLeft.x + 'px';
this.element.style.top = this.pointTopLeft.y + 'px';
this.element.style.width = this.pointBottomRight.x - this.pointTopLeft.x + 'px';
this.element.style.height = this.pointBottomRight.y - this.pointTopLeft.y + 'px';
}

onSelectOver() {
if (this.element.parentElement) {
this.element.parentElement.removeChild(this.element);
}
}
}

export { SelectionHelper };
58 changes: 45 additions & 13 deletions src/lib/card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { splitProps } from 'solid-js';
import {
BoxGeometry,
Color,
ImageBitmapLoader,
LinearFilter,
Mesh,
MeshStandardMaterial,
Expand All @@ -15,29 +14,61 @@ import {
Vector3,
} from 'three';
import { Card, CARD_HEIGHT, CARD_STACK_OFFSET, CARD_THICKNESS, CARD_WIDTH } from './constants';
import { cardsById, getProjectionVec, scene, textureLoader, textureLoaderWorker } from './globals';
import {
cardBackTexture,
cardLoadingTexture,
cardsById,
getProjectionVec,
scene,
textureLoader,
textureLoaderWorker,
} from './globals';
import { counters } from './ui/counterDialog';
import { cleanupFromNode } from './utils';

let cardBackTexture: Texture;
let alphaMap: Texture;
const blackMat = new MeshStandardMaterial({ color: 0x000000 });

const bitmapLoader = new ImageBitmapLoader();
bitmapLoader.setOptions({ imageOrientation: 'flipY' });
let currentSlide = 0;
let totalSlides = 6;
let xSlides = 3;
let ySlides = 2;
let ticks = 0;
let interval = 1 / 7;

export function updateTextureAnimation(delta: number) {
ticks += delta;
if (ticks < interval) return;
ticks %= interval;
if (!cardLoadingTexture) return;
let x = (currentSlide % xSlides) / xSlides;
let y = ((currentSlide / xSlides) | 0) / ySlides;
cardLoadingTexture.offset.y = y;
cardLoadingTexture.offset.x = x;
currentSlide++;
currentSlide = currentSlide % totalSlides;
}

export function createCardGeometry(card: Card, cache?: Map<string, ImageBitmap>) {
const geometry = new BoxGeometry(CARD_WIDTH, CARD_HEIGHT, CARD_THICKNESS);
cardBackTexture = cardBackTexture || textureLoader.load('/arcane-table-back.webp');
cardBackTexture.colorSpace = SRGBColorSpace;
let cardBackMat = new MeshStandardMaterial({ map: cardBackTexture });
let { mesh: _, modifiers, ...shared } = card;

cardBackMat.transparent = true;

let loadingMat = new MeshStandardMaterial({ map: cardLoadingTexture, alphaMap });
loadingMat.transparent = true;

let { mesh: _, modifiers, ...shared } = card;

alphaMap = alphaMap || textureLoader.load(`/alphaMap.webp`);

const mesh = new Mesh(geometry, [blackMat, blackMat, blackMat, blackMat, blackMat, cardBackMat]);
const mesh = new Mesh(geometry, [
blackMat.clone(),
blackMat.clone(),
blackMat.clone(),
blackMat.clone(),
loadingMat.clone(),
cardBackMat.clone(),
]);
setCardData(mesh, 'isInteractive', true);
setCardData(mesh, 'card', shared);
setCardData(mesh, 'id', card.id);
Expand All @@ -60,7 +91,7 @@ export function createCardGeometry(card: Card, cache?: Map<string, ImageBitmap>)

export async function loadCardTextures(
card: Card,
cache: Map<string, Promise<MeshStandardMaterial>>
cache: Map<string, Promise<MeshStandardMaterial>> = new Map()
) {
const [front, back] = card.mesh.userData.card_face_urls;

Expand All @@ -87,7 +118,7 @@ export async function loadCardTextures(
let frontPromise = cache.get(front)!;

frontPromise.then(mat => {
card.mesh.material[4] = mat;
card.mesh.material[4] = mat.clone();
});

if (back) {
Expand Down Expand Up @@ -115,7 +146,7 @@ export async function loadCardTextures(
let backPromise = cache.get(back)!;

backPromise.then(mat => {
card.mesh.userData.cardBack = mat;
card.mesh.userData.cardBack = mat.clone();
});
await backPromise;
}
Expand Down Expand Up @@ -161,6 +192,7 @@ export function cloneCard(card: Card, newId: string): Card {
updateModifiers(newCard);
newCard.detail.search = card.detail.search ?? getSearchLine(newCard.detail);
cardsById.set(newCard.id, newCard);
loadCardTextures(newCard);
return newCard;
}

Expand Down
18 changes: 15 additions & 3 deletions src/lib/cardArea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@ import {
} from 'three';
import { animateObject } from './animations';
import { getSerializableCard, setCardData } from './card';
import { Card, CARD_HEIGHT, CARD_STACK_OFFSET, CARD_THICKNESS, CardZone } from './constants';
import {
Card,
CARD_HEIGHT,
CARD_STACK_OFFSET,
CARD_THICKNESS,
CARD_ZONE_COLOR,
CardZone,
ZONE_OUTLINE_COLOR,
} from './constants';
import { cardsById, zonesById } from './globals';
import { cleanupMesh, getGlobalRotation } from './utils';

Expand All @@ -27,15 +35,19 @@ export class CardArea implements CardZone<{ positionArray?: [number, number, num

constructor(public zone: string, public id: string = nanoid()) {
let geometry = new BoxGeometry(200, 100, CARD_THICKNESS / 2);
let material = new MeshStandardMaterial({ color: 0x2b2d3a }); //#9d9eae // 1e2029
let material = new MeshStandardMaterial({ color: CARD_ZONE_COLOR }); //#9d9eae // 1e2029
this.mesh = new Mesh(geometry, material);
this.mesh.userData.zone = zone;
this.mesh.userData.zoneId = id;
this.mesh.userData.id = id;
this.cards = [];
this.mesh.position.setY(-50);
this.mesh.receiveShadow = true;
let edges = new EdgesGeometry(geometry);
let lineSegments = new LineSegments(edges, new LineBasicMaterial({ color: 0xffffff }));
let lineSegments = new LineSegments(
edges,
new LineBasicMaterial({ color: ZONE_OUTLINE_COLOR })
);
lineSegments.userData.isOrnament = true;
lineSegments.position.setZ(0.125);
this.mesh.add(lineSegments);
Expand Down
3 changes: 1 addition & 2 deletions src/lib/cardGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export class CardGrid implements CardZone {
zonesById.set(this.id, this);
this.mesh.userData.isInteractive = true;
this.mesh.userData.zone = zone;
this.mesh.userData.id = id;
this.mesh.rotateX(Math.PI * 0.25);
this.mesh.position.copy(POSITION);
this.scrollContainer = new Group();
Expand Down Expand Up @@ -289,12 +290,10 @@ export class CardGrid implements CardZone {
if (this.cards.length < 1) {
setHoverSignal();
}
console.log(peekFilterText());
if (this.filteredCards) {
this.filteredCards = this.filteredCards.filter(card => card.id !== cardMesh.userData.id);
}
if (!this.filteredCards?.length) {
console.log(this.filteredCards);
setPeekFilterText('');
this.filterCards();
}
Expand Down
15 changes: 13 additions & 2 deletions src/lib/cardStack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ import {
} from 'three';
import { animateObject } from './animations';
import { cleanupCard, getSerializableCard, setCardData } from './card';
import { Card, CARD_HEIGHT, CARD_THICKNESS, CARD_WIDTH, CardZone } from './constants';
import {
Card,
CARD_HEIGHT,
CARD_THICKNESS,
CARD_WIDTH,
CardZone,
ZONE_OUTLINE_COLOR,
} from './constants';
import { cardsById, setHoverSignal, zonesById } from './globals';
import { cleanupMesh, getGlobalRotation } from './utils';

Expand All @@ -29,7 +36,10 @@ export class CardStack implements CardZone {
let geometry = new BoxGeometry(CARD_WIDTH, CARD_HEIGHT, CARD_THICKNESS);
let material = new MeshStandardMaterial({ color: 0x000000 });
let edges = new EdgesGeometry(geometry);
let lineSegments = new LineSegments(edges, new LineBasicMaterial({ color: 0xffffff }));
let lineSegments = new LineSegments(
edges,
new LineBasicMaterial({ color: ZONE_OUTLINE_COLOR })
);
lineSegments.scale.set(1.1, 1.1, 1);
lineSegments.userData.isOrnament = true;
material.opacity = 0;
Expand All @@ -38,6 +48,7 @@ export class CardStack implements CardZone {
this.mesh.add(lineSegments);
this.mesh.userData.zone = zone;
this.mesh.userData.zoneId = id;
this.mesh.userData.id = id;
createRoot(destroy => {
this.destroyReactivity = destroy;

Expand Down
3 changes: 3 additions & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export const CARD_HEIGHT = 88 / 4;
export const CARD_THICKNESS = 0.5 / 4;
export const CARD_STACK_OFFSET = 2;

export const ZONE_OUTLINE_COLOR = 0xffffff;
export const CARD_ZONE_COLOR = 0x1a1533;

export interface Card {
mesh: Mesh;
id: string;
Expand Down
1 change: 1 addition & 0 deletions src/lib/deck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class Deck implements CardZone<{ location: 'top' | 'bottom' }> {
this.mesh.position.set(70, -55, cards.length * 0.125 + 2.5);
this.mesh.userData.isInteractive = true;
this.mesh.userData.zone = 'deck';
this.mesh.userData.id = id;
this.zone = 'deck';

cards.forEach((card, i) => {
Expand Down
Loading

0 comments on commit 39e4808

Please sign in to comment.