Tingchenliang commited on
Commit
dd4c5da
·
verified ·
1 Parent(s): 2b82b21

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. assets/css/styles.css +132 -0
  2. assets/js/script.js +248 -0
  3. index.html +72 -19
assets/css/styles.css ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ body {
8
+ font-family: 'Courier New', monospace;
9
+ line-height: 1.6;
10
+ color: #333;
11
+ background-color: #f4f4f4;
12
+ }
13
+
14
+ header {
15
+ background: #333;
16
+ color: #fff;
17
+ padding: 1rem;
18
+ text-align: center;
19
+ }
20
+
21
+ header h1 {
22
+ font-size: 1.8rem;
23
+ }
24
+
25
+ .container {
26
+ display: flex;
27
+ flex-direction: column;
28
+ padding: 1rem;
29
+ gap: 1rem;
30
+ max-width: 1200px;
31
+ margin: 0 auto;
32
+ }
33
+
34
+ .viewer-container {
35
+ position: relative;
36
+ width: 100%;
37
+ background: #000;
38
+ border-radius: 8px;
39
+ overflow: hidden;
40
+ min-height: 400px;
41
+ }
42
+
43
+ #ascii-canvas {
44
+ width: 100%;
45
+ height: 100%;
46
+ font-family: 'Courier New', monospace;
47
+ font-size: 8px;
48
+ line-height: 1;
49
+ letter-spacing: 0;
50
+ color: #fff;
51
+ white-space: pre;
52
+ }
53
+
54
+ .loading {
55
+ position: absolute;
56
+ top: 50%;
57
+ left: 50%;
58
+ transform: translate(-50%, -50%);
59
+ color: #fff;
60
+ font-size: 1.2rem;
61
+ }
62
+
63
+ .controls-panel {
64
+ background: #fff;
65
+ padding: 1.5rem;
66
+ border-radius: 8px;
67
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
68
+ }
69
+
70
+ .control-group {
71
+ margin-bottom: 1.5rem;
72
+ }
73
+
74
+ .control-group h3 {
75
+ margin-bottom: 0.8rem;
76
+ border-bottom: 1px solid #eee;
77
+ padding-bottom: 0.5rem;
78
+ }
79
+
80
+ label {
81
+ display: block;
82
+ margin-bottom: 0.5rem;
83
+ font-weight: bold;
84
+ }
85
+
86
+ select, input[type="range"], button {
87
+ width: 100%;
88
+ padding: 0.5rem;
89
+ margin-bottom: 1rem;
90
+ border: 1px solid #ddd;
91
+ border-radius: 4px;
92
+ font-family: inherit;
93
+ }
94
+
95
+ button {
96
+ background: #333;
97
+ color: #fff;
98
+ border: none;
99
+ cursor: pointer;
100
+ transition: background 0.3s;
101
+ }
102
+
103
+ button:hover {
104
+ background: #555;
105
+ }
106
+
107
+ input[type="checkbox"] {
108
+ margin-right: 0.5rem;
109
+ }
110
+
111
+ footer {
112
+ text-align: center;
113
+ padding: 1rem;
114
+ background: #333;
115
+ color: #fff;
116
+ margin-top: 2rem;
117
+ }
118
+
119
+ @media (min-width: 768px) {
120
+ .container {
121
+ flex-direction: row;
122
+ }
123
+
124
+ .viewer-container {
125
+ flex: 2;
126
+ }
127
+
128
+ .controls-panel {
129
+ flex: 1;
130
+ min-width: 300px;
131
+ }
132
+ }
assets/js/script.js ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class ASCII3DViewer {
2
+ constructor() {
3
+ this.scene = new THREE.Scene();
4
+ this.camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
5
+ this.renderer = new THREE.WebGLRenderer();
6
+ this.asciiCanvas = document.getElementById('ascii-canvas');
7
+ this.context = this.asciiCanvas.getContext('2d');
8
+
9
+ this.currentModel = null;
10
+ this.autoRotate = true;
11
+ this.resolution = 50;
12
+ this.charSet = '@%#*+=-:. ';
13
+ this.colorMode = 'mono';
14
+
15
+ this.init();
16
+ this.setupEventListeners();
17
+ this.loadModel('cube');
18
+ this.animate();
19
+ }
20
+
21
+ init() {
22
+ this.camera.position.z = 5;
23
+ this.scene.background = new THREE.Color(0x000000);
24
+
25
+ // Add some ambient light
26
+ const ambientLight = new THREE.AmbientLight(0x404040);
27
+ this.scene.add(ambientLight);
28
+
29
+ // Add directional light
30
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
31
+ directionalLight.position.set(1, 1, 1);
32
+ this.scene.add(directionalLight);
33
+
34
+ this.resize();
35
+ window.addEventListener('resize', () => this.resize());
36
+ }
37
+
38
+ resize() {
39
+ const container = this.asciiCanvas.parentElement;
40
+ const width = container.clientWidth;
41
+ const height = container.clientHeight;
42
+
43
+ this.asciiCanvas.width = width;
44
+ this.asciiCanvas.height = height;
45
+ this.camera.aspect = width / height;
46
+ this.camera.updateProjectionMatrix();
47
+ }
48
+
49
+ loadModel(modelType) {
50
+ // Remove current model if exists
51
+ if (this.currentModel) {
52
+ this.scene.remove(this.currentModel);
53
+ }
54
+
55
+ let geometry;
56
+
57
+ switch(modelType) {
58
+ case 'sphere':
59
+ geometry = new THREE.SphereGeometry(1, 32, 32);
60
+ break;
61
+ case 'torus':
62
+ geometry = new THREE.TorusGeometry(1, 0.4, 16, 100);
63
+ break;
64
+ case 'teapot':
65
+ // Simple teapot approximation
66
+ geometry = new THREE.CylinderGeometry(0.5, 0.8, 1, 8);
67
+ const lid = new THREE.CylinderGeometry(0.3, 0.5, 0.3, 8);
68
+ lid.translate(0, 0.8, 0);
69
+ geometry = THREE.BufferGeometryUtils.mergeBufferGeometries([geometry, lid]);
70
+ break;
71
+ case 'cube':
72
+ default:
73
+ geometry = new THREE.BoxGeometry(1, 1, 1);
74
+ break;
75
+ }
76
+
77
+ const material = new THREE.MeshPhongMaterial({
78
+ color: 0xffffff,
79
+ flatShading: true
80
+ });
81
+
82
+ this.currentModel = new THREE.Mesh(geometry, material);
83
+ this.scene.add(this.currentModel);
84
+
85
+ document.getElementById('loading').style.display = 'none';
86
+ }
87
+
88
+ renderToASCII() {
89
+ const width = this.asciiCanvas.width;
90
+ const height = this.asciiCanvas.height;
91
+
92
+ // Temporary render to get pixel data
93
+ this.renderer.setSize(width, height);
94
+ this.renderer.render(this.scene, this.camera);
95
+
96
+ // Create offscreen buffer
97
+ const buffer = document.createElement('canvas');
98
+ buffer.width = width;
99
+ buffer.height = height;
100
+ const bufferCtx = buffer.getContext('2d');
101
+ bufferCtx.drawImage(this.renderer.domElement, 0, 0, width, height);
102
+
103
+ const imageData = bufferCtx.getImageData(0, 0, width, height);
104
+ const pixels = imageData.data;
105
+
106
+ // Clear canvas
107
+ this.context.fillStyle = '#000';
108
+ this.context.fillRect(0, 0, width, height);
109
+
110
+ // Calculate step based on resolution
111
+ const step = Math.max(1, Math.floor(100 / this.resolution));
112
+ const chars = this.charSet;
113
+ const charCount = chars.length;
114
+
115
+ // Render ASCII art
116
+ this.context.font = `${step}px 'Courier New', monospace`;
117
+ this.context.textAlign = 'center';
118
+
119
+ for (let y = 0; y < height; y += step * 2) {
120
+ for (let x = 0; x < width; x += step) {
121
+ const index = (y * width + x) * 4;
122
+ const r = pixels[index];
123
+ const g = pixels[index + 1];
124
+ const b = pixels[index + 2];
125
+
126
+ if (r + g + b > 10) { // Skip very dark pixels
127
+ const brightness = (r + g + b) / 3 / 255;
128
+ const charIndex = Math.floor(brightness * (charCount - 1));
129
+ const char = chars[charIndex];
130
+
131
+ // Set color based on mode
132
+ if (this.colorMode === 'color') {
133
+ this.context.fillStyle = `rgb(${r},${g},${b})`;
134
+ } else if (this.colorMode === 'grayscale') {
135
+ const gray = (r + g + b) / 3;
136
+ this.context.fillStyle = `rgb(${gray},${gray},${gray})`;
137
+ } else {
138
+ this.context.fillStyle = '#fff';
139
+ }
140
+
141
+ this.context.fillText(char, x, y);
142
+ }
143
+ }
144
+ }
145
+ }
146
+
147
+ animate() {
148
+ requestAnimationFrame(() => this.animate());
149
+
150
+ if (this.autoRotate && this.currentModel) {
151
+ this.currentModel.rotation.x += 0.01;
152
+ this.currentModel.rotation.y += 0.01;
153
+ }
154
+
155
+ this.renderToASCII();
156
+ }
157
+
158
+ setupEventListeners() {
159
+ // Model selection
160
+ document.getElementById('model-select').addEventListener('change', (e) => {
161
+ document.getElementById('loading').style.display = 'block';
162
+ setTimeout(() => this.loadModel(e.target.value), 100);
163
+ });
164
+
165
+ // Resolution slider
166
+ const resolutionSlider = document.getElementById('resolution');
167
+ const resolutionValue = document.getElementById('resolution-value');
168
+
169
+ resolutionSlider.addEventListener('input', (e) => {
170
+ this.resolution = parseInt(e.target.value);
171
+ resolutionValue.textContent = this.resolution;
172
+ });
173
+
174
+ // Character set
175
+ document.getElementById('charset').addEventListener('change', (e) => {
176
+ const sets = {
177
+ 'default': '@%#*+=-:. ',
178
+ 'dense': '█▓▒░ ',
179
+ 'simple': '#*+-. ',
180
+ 'numbers': '9876543210'
181
+ };
182
+ this.charSet = sets[e.target.value];
183
+ });
184
+
185
+ // Color mode
186
+ document.getElementById('color-mode').addEventListener('change', (e) => {
187
+ this.colorMode = e.target.value;
188
+ });
189
+
190
+ // Auto rotation
191
+ document.getElementById('auto-rotate').addEventListener('change', (e) => {
192
+ this.autoRotate = e.target.checked;
193
+ });
194
+
195
+ // Reset camera
196
+ document.getElementById('reset-camera').addEventListener('click', () => {
197
+ if (this.currentModel) {
198
+ this.currentModel.rotation.set(0, 0, 0);
199
+ }
200
+ });
201
+
202
+ // Mouse controls for manual rotation
203
+ let isDragging = false;
204
+ let previousMousePosition = {x: 0, y: 0};
205
+
206
+ this.asciiCanvas.addEventListener('mousedown', (e) => {
207
+ isDragging = true;
208
+ });
209
+
210
+ this.asciiCanvas.addEventListener('mouseup', () => {
211
+ isDragging = false;
212
+ });
213
+
214
+ this.asciiCanvas.addEventListener('mousemove', (e) => {
215
+ if (!isDragging || !this.currentModel) return;
216
+
217
+ const deltaMove = {
218
+ x: e.offsetX - previousMousePosition.x,
219
+ y: e.offsetY - previousMousePosition.y
220
+ };
221
+
222
+ this.currentModel.rotation.y += deltaMove.x * 0.01;
223
+ this.currentModel.rotation.x += deltaMove.y * 0.01;
224
+
225
+ previousMousePosition = {
226
+ x: e.offsetX,
227
+ y: e.offsetY
228
+ };
229
+ });
230
+
231
+ this.asciiCanvas.addEventListener('mouseleave', () => {
232
+ isDragging = false;
233
+ });
234
+ }
235
+ }
236
+
237
+ // Initialize the viewer when the page loads
238
+ window.addEventListener('load', () => {
239
+ new ASCII3DViewer();
240
+ });
241
+
242
+ // Polyfill for BufferGeometryUtils if needed
243
+ if (!THREE.BufferGeometryUtils) {
244
+ console.warn('THREE.BufferGeometryUtils not available. Some models may not work correctly.');
245
+ THREE.BufferGeometryUtils = {
246
+ mergeBufferGeometries: (geometries) => geometries[0]
247
+ };
248
+ }
index.html CHANGED
@@ -1,19 +1,72 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>3D ASCII Model Viewer</title>
7
+ <meta name="description" content="Interactive 3D ASCII art model viewer with customizable settings">
8
+ <link rel="stylesheet" href="assets/css/styles.css">
9
+ </head>
10
+ <body>
11
+ <header>
12
+ <h1>3D ASCII Model Viewer</h1>
13
+ </header>
14
+
15
+ <main class="container">
16
+ <div class="viewer-container">
17
+ <canvas id="ascii-canvas"></canvas>
18
+ <div id="loading" class="loading">Loading...</div>
19
+ </div>
20
+
21
+ <div class="controls-panel">
22
+ <div class="control-group">
23
+ <label for="model-select">Select Model:</label>
24
+ <select id="model-select">
25
+ <option value="cube">Cube</option>
26
+ <option value="sphere">Sphere</option>
27
+ <option value="torus">Torus</option>
28
+ <option value="teapot">Teapot</option>
29
+ </select>
30
+ </div>
31
+
32
+ <div class="control-group">
33
+ <h3>ASCII Settings</h3>
34
+ <label for="resolution">Resolution:</label>
35
+ <input type="range" id="resolution" min="10" max="100" value="50">
36
+ <span id="resolution-value">50</span>
37
+
38
+ <label for="charset">Character Set:</label>
39
+ <select id="charset">
40
+ <option value="default">@%#*+=-:. </option>
41
+ <option value="dense">█▓▒░ </option>
42
+ <option value="simple">#*+-. </option>
43
+ <option value="numbers">9876543210</option>
44
+ </select>
45
+
46
+ <label for="color-mode">Color Mode:</label>
47
+ <select id="color-mode">
48
+ <option value="mono">Monochrome</option>
49
+ <option value="grayscale">Grayscale</option>
50
+ <option value="color">Full Color</option>
51
+ </select>
52
+ </div>
53
+
54
+ <div class="control-group">
55
+ <h3>Camera Controls</h3>
56
+ <label>
57
+ <input type="checkbox" id="auto-rotate" checked>
58
+ Auto Rotate
59
+ </label>
60
+ <button id="reset-camera">Reset Camera</button>
61
+ </div>
62
+ </div>
63
+ </main>
64
+
65
+ <footer>
66
+ <p>&copy; 2023 3D ASCII Model Viewer | Built with Three.js</p>
67
+ </footer>
68
+
69
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
70
+ <script src="assets/js/script.js"></script>
71
+ </body>
72
+ </html>