mirror of https://github.com/aziis98/ro-vis
initial commit
commit
be437c2858
@ -0,0 +1,15 @@
|
|||||||
|
# Local files
|
||||||
|
.env
|
||||||
|
*.local*
|
||||||
|
|
||||||
|
# NodeJS
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
bin/
|
||||||
|
.out/
|
||||||
|
out/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Editors
|
||||||
|
.vscode/
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
# gem "rails"
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
x86_64-linux
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.5.17
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
# Ricerca Operativa
|
||||||
|
|
||||||
|
Un piccolo sito con alcuni degli algoritmi presentati nel corso di Ricerca Operativa. Gli algoritmi sono scritti in Ruby e
|
||||||
|
eseguiti nel browser con WASM (uso il Ruby perché è l'unico linguaggio con i numeri razionali e buon supporto per WASM).
|
||||||
|
|
||||||
|
## Algoritmi
|
||||||
|
|
||||||
|
### Programmazione Lineare
|
||||||
|
|
||||||
|
- [ ] Metodo del simplesso
|
||||||
|
|
||||||
|
- [ ] Metodo del simplesso (con i conti)
|
||||||
|
|
||||||
|
- [ ] Metodo del simplesso (con disegnini)
|
||||||
|
|
||||||
|
- [ ] Metodo del simplesso duale
|
||||||
|
|
||||||
|
- [ ] Metodo del simplesso duale (con i conti)
|
||||||
|
|
||||||
|
- [ ] Metodo del simplesso duale (con disegnini)
|
||||||
|
|
||||||
|
### Programmazione Lineare Intera
|
||||||
|
|
||||||
|
- [ ] Branch and Bound
|
||||||
|
|
||||||
|
- [ ] Branch and Cut
|
||||||
|
|
||||||
|
- [ ] Branch and Price
|
||||||
|
|
||||||
|
### Programmazione Dinamica
|
||||||
|
|
||||||
|
- [ ] Algoritmo di Bellman
|
||||||
|
|
||||||
|
- [ ] Algoritmo di Dijkstra
|
||||||
|
|
||||||
|
- [ ] Algoritmo di Floyd
|
||||||
|
|
||||||
|
### Altri
|
||||||
|
|
||||||
|
- [ ] Algoritmo di Kruskal
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run index.ts
|
||||||
|
```
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
||||||
|
<meta property="og:title" content="Visualizzazioni di Ricerca Operativa" />
|
||||||
|
<meta property="og:description" content="Visualizzazioni di Ricerca Operativa" />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
|
||||||
|
<title>Visualizzazioni di Ricerca Operativa</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="module" src="./src/main.jsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "ricerca-operativa",
|
||||||
|
"module": "index.ts",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@preact/preset-vite": "^2.9.0",
|
||||||
|
"@types/bun": "latest",
|
||||||
|
"@types/katex": "^0.16.7",
|
||||||
|
"@types/lodash": "^4.17.7",
|
||||||
|
"vite": "^5.4.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@fontsource/inter": "^5.0.20",
|
||||||
|
"@ruby/3.3-wasm-wasi": "^2.6.2",
|
||||||
|
"@ruby/wasm-wasi": "^2.6.2",
|
||||||
|
"katex": "^0.16.11",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"vite-plugin-wasm": "^3.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
import { Steps } from '../../components/Steps.jsx'
|
||||||
|
import { evalRuby } from '../../ruby.js'
|
||||||
|
import algorithmCode from './algorithm.rb?raw'
|
||||||
|
|
||||||
|
import { useEffect, useState } from 'preact/hooks'
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
group: 'Programmazione Lineare',
|
||||||
|
title: 'Simplesso Primale Algebrico',
|
||||||
|
description: 'Algoritmo principale della programmazione lineare',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const View = ({}) => {
|
||||||
|
const [steps, setSteps] = useState([])
|
||||||
|
|
||||||
|
useEffect(async () => {
|
||||||
|
const { outputs } = await evalRuby(algorithmCode)
|
||||||
|
setSteps(outputs)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1>Simplesso Primale Algebrico</h1>
|
||||||
|
<p>TODO</p>
|
||||||
|
|
||||||
|
<Steps steps={steps} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
require "json"
|
||||||
|
require "js"
|
||||||
|
|
||||||
|
def print_step(label, state)
|
||||||
|
puts "Ruby: #{label} #{state}"
|
||||||
|
JS.global[:rubyOutputs].push({
|
||||||
|
label: label,
|
||||||
|
state: state
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(config)
|
||||||
|
i = config["i"] || 1
|
||||||
|
j = config["j"] || 1
|
||||||
|
n = config["n"] || 10
|
||||||
|
|
||||||
|
print_step "Initial", { i: i, j: j, n: n }
|
||||||
|
|
||||||
|
n.times do
|
||||||
|
m = i + j
|
||||||
|
i = j
|
||||||
|
j = m
|
||||||
|
|
||||||
|
print_step "Step", { i: i, j: j }
|
||||||
|
end
|
||||||
|
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
|
||||||
|
run({ "n" => 10 })
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import 'katex/dist/katex.min.css'
|
||||||
|
import katex from 'katex'
|
||||||
|
|
||||||
|
export const KaTeX = ({ source }) => {
|
||||||
|
console.log(source)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class="katex"
|
||||||
|
ref={el => {
|
||||||
|
if (!el) return
|
||||||
|
katex.render(source.toString(), el, { throwOnError: false })
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { KaTeX } from './KaTeX.jsx'
|
||||||
|
|
||||||
|
export const Steps = ({ steps }) => {
|
||||||
|
return (
|
||||||
|
<div class="steps">
|
||||||
|
{steps.map((step, i) => (
|
||||||
|
<div class="step">
|
||||||
|
<div class="label">{step.label}</div>
|
||||||
|
<div class="state">
|
||||||
|
{Object.entries(step.state).map(([key, value]) => (
|
||||||
|
<KaTeX source={`${key} = ${value}`} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -0,0 +1,105 @@
|
|||||||
|
import './style.css'
|
||||||
|
import '@fontsource/inter/latin.css'
|
||||||
|
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
import { render } from 'preact'
|
||||||
|
import { useState } from 'preact/hooks'
|
||||||
|
|
||||||
|
const ViewRegistry = Object.fromEntries(
|
||||||
|
Object.entries(
|
||||||
|
import.meta.glob('./algorithms/*/View.jsx', {
|
||||||
|
eager: true,
|
||||||
|
})
|
||||||
|
/* fix for broken syntax highlighting */
|
||||||
|
).map(([path, module]) => {
|
||||||
|
const [id] = path.match(/(?<=\/)[^/]*(?=\/View\.jsx$)/)
|
||||||
|
return [id, { id, ...module }]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const NewAlgorithmBox = ({ title, description, onClick }) => (
|
||||||
|
<div class="algorithm-box" onClick={onClick}>
|
||||||
|
<h3>{title}</h3>
|
||||||
|
<p>{description}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const AlgorithmChooserView = ({ setCurrentView }) => {
|
||||||
|
const sections = _.groupBy(ViewRegistry, 'metadata.group')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1>Algoritmi</h1>
|
||||||
|
{Object.entries(sections).map(([group, algorithms]) => (
|
||||||
|
<section>
|
||||||
|
<h2>{group}</h2>
|
||||||
|
<div class="boxes">
|
||||||
|
{algorithms.map(({ id, metadata }) => (
|
||||||
|
<NewAlgorithmBox
|
||||||
|
title={metadata.title}
|
||||||
|
description={metadata.description}
|
||||||
|
onClick={() => setCurrentView(id)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
))}
|
||||||
|
<section>
|
||||||
|
<h2>Flussi su Grafi</h2>
|
||||||
|
<div class="boxes">
|
||||||
|
<NewAlgorithmBox title="TODO" description="lorem ipsum dolor sit amet" />
|
||||||
|
<NewAlgorithmBox title="TODO" description="lorem ipsum dolor sit amet" />
|
||||||
|
<NewAlgorithmBox title="TODO" description="lorem ipsum dolor sit amet" />
|
||||||
|
<NewAlgorithmBox title="TODO" description="lorem ipsum dolor sit amet" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>Flussi su Grafi</h2>
|
||||||
|
<div class="boxes">
|
||||||
|
<NewAlgorithmBox title="TODO" description="lorem ipsum dolor sit amet" />
|
||||||
|
<NewAlgorithmBox title="TODO" description="lorem ipsum dolor sit amet" />
|
||||||
|
<NewAlgorithmBox title="TODO" description="lorem ipsum dolor sit amet" />
|
||||||
|
<NewAlgorithmBox title="TODO" description="lorem ipsum dolor sit amet" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>Flussi su Grafi</h2>
|
||||||
|
<div class="boxes">
|
||||||
|
<NewAlgorithmBox title="TODO" description="lorem ipsum dolor sit amet" />
|
||||||
|
<NewAlgorithmBox title="TODO" description="lorem ipsum dolor sit amet" />
|
||||||
|
<NewAlgorithmBox title="TODO" description="lorem ipsum dolor sit amet" />
|
||||||
|
<NewAlgorithmBox title="TODO" description="lorem ipsum dolor sit amet" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Main = ({}) => {
|
||||||
|
const [currentView, setCurrentView] = useState(null)
|
||||||
|
|
||||||
|
if (!currentView) {
|
||||||
|
return <AlgorithmChooserView setCurrentView={setCurrentView} />
|
||||||
|
}
|
||||||
|
|
||||||
|
const View = ViewRegistry[currentView].View
|
||||||
|
|
||||||
|
return <View />
|
||||||
|
}
|
||||||
|
|
||||||
|
const App = ({}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<header>Visualizzazioni di Ricerca Operativa</header>
|
||||||
|
<aside>
|
||||||
|
<h2>History</h2>
|
||||||
|
</aside>
|
||||||
|
<main>
|
||||||
|
<Main />
|
||||||
|
</main>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render(<App />, document.body)
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
import rubyWasmModuleUrl from '@ruby/3.3-wasm-wasi/dist/ruby+stdlib.wasm?url'
|
||||||
|
import { DefaultRubyVM } from '@ruby/wasm-wasi/dist/browser'
|
||||||
|
|
||||||
|
window.rubyOutputs = []
|
||||||
|
let vmInstance = null
|
||||||
|
|
||||||
|
async function preloadRuby() {
|
||||||
|
if (vmInstance) return
|
||||||
|
|
||||||
|
const response = await fetch(rubyWasmModuleUrl)
|
||||||
|
const module = await WebAssembly.compileStreaming(response)
|
||||||
|
const { vm } = await DefaultRubyVM(module)
|
||||||
|
vmInstance = vm
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function evalRuby(code) {
|
||||||
|
await preloadRuby()
|
||||||
|
|
||||||
|
window.rubyOutputs = []
|
||||||
|
const result = vmInstance.eval(code)
|
||||||
|
|
||||||
|
return {
|
||||||
|
result,
|
||||||
|
outputs: window.rubyOutputs,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,176 @@
|
|||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
font-family: inherit;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
min-height: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Typography */
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Components */
|
||||||
|
|
||||||
|
.boxes {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
align-items: start;
|
||||||
|
|
||||||
|
.algorithm-box {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
|
||||||
|
gap: 0.25rem;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
|
||||||
|
background: #fff;
|
||||||
|
border: 2px solid #333;
|
||||||
|
|
||||||
|
width: 10rem;
|
||||||
|
min-height: calc(10rem * 3 / 4);
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f4f4f4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.steps {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
|
||||||
|
place-self: center;
|
||||||
|
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
.step {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
|
||||||
|
background: #fff;
|
||||||
|
border: 2px solid #333;
|
||||||
|
|
||||||
|
min-width: 20rem;
|
||||||
|
|
||||||
|
& > .label {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .state {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Structure */
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 20% 1fr;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
background: #f0f0f0;
|
||||||
|
|
||||||
|
& > header,
|
||||||
|
aside,
|
||||||
|
main,
|
||||||
|
footer {
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > header {
|
||||||
|
height: 3rem;
|
||||||
|
|
||||||
|
grid-column: span 2;
|
||||||
|
|
||||||
|
border-bottom: 2px solid #333;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > aside {
|
||||||
|
border-right: 2px solid #333;
|
||||||
|
background: #e0e0e0;
|
||||||
|
|
||||||
|
position: sticky;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
align-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > main {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto;
|
||||||
|
|
||||||
|
padding: 6rem 1rem;
|
||||||
|
gap: 1.5rem;
|
||||||
|
|
||||||
|
place-content: center;
|
||||||
|
align-content: start;
|
||||||
|
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
& > section {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Enable latest features
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
|
||||||
|
import wasm from 'vite-plugin-wasm'
|
||||||
|
import preactPlugin from '@preact/preset-vite'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
build: {
|
||||||
|
outDir: 'out',
|
||||||
|
emptyOutDir: true,
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
port: 3000,
|
||||||
|
},
|
||||||
|
plugins: [preactPlugin(), wasm()],
|
||||||
|
})
|
||||||
Loading…
Reference in New Issue