From e3a5f83f5bd120ffb0b53c2861264906fe76542f Mon Sep 17 00:00:00 2001
From: Antonio De Lucreziis
Date: Mon, 20 Jan 2025 23:35:01 +0100
Subject: [PATCH] fix: retina canvas support
---
src/{Primale.tsx => Primal.tsx} | 138 ++++++++++++++++----------------
src/lib-v2/canvas/index.ts | 2 +-
src/main.tsx | 5 +-
3 files changed, 75 insertions(+), 70 deletions(-)
rename src/{Primale.tsx => Primal.tsx} (83%)
diff --git a/src/Primale.tsx b/src/Primal.tsx
similarity index 83%
rename from src/Primale.tsx
rename to src/Primal.tsx
index 9e4fd5b..1006138 100644
--- a/src/Primale.tsx
+++ b/src/Primal.tsx
@@ -82,7 +82,7 @@ const PrimalStep = ({
)
}
-export const Primale = ({ input }: { input: ProblemInput }) => {
+export const Primal = ({ input }: { input: ProblemInput }) => {
// const steps: Step[] = [{ B: input.B }]
const problemOutput = computePrimalSimplexSteps({
@@ -141,16 +141,16 @@ const PrimalCanvas = ({
return
}
- $canvas.width = $canvas.offsetWidth
- $canvas.height = $canvas.offsetHeight
+ $canvas.width = $canvas.offsetWidth * window.devicePixelRatio
+ $canvas.height = $canvas.offsetHeight * window.devicePixelRatio
const g = $canvas.getContext('2d')
if (!g) {
throw new Error('Could not get 2d context')
}
- const width = $canvas.width
- const height = $canvas.height
+ const width = $canvas.offsetWidth
+ const height = $canvas.offsetHeight
g.clearRect(0, 0, width, height)
g.strokeStyle = '#333'
@@ -163,6 +163,8 @@ const PrimalCanvas = ({
g.textAlign = 'center'
g.textBaseline = 'middle'
+ g.scale(window.devicePixelRatio, window.devicePixelRatio)
+
const [c1, c2] = c.getData()
const cLen = Math.sqrt(c1.toNumber() ** 2 + c2.toNumber() ** 2)
@@ -202,87 +204,89 @@ const PrimalCanvas = ({
// g.textBaseline = 'middle'
// g.fillText(`A = ${A}`, width / 2, height / 2)
- g.translate(width / 2, height / 2)
- g.scale(width / 2, -width / 2)
- g.scale(1 / 10, 1 / 10)
+ g.save()
+ {
+ g.translate(width / 2, height / 2)
+ g.scale(width / 2, -width / 2)
+ g.scale(1 / 10, 1 / 10)
- // draw grid
+ // draw grid
- g.strokeStyle = '#ddd'
- g.lineWidth = 20 / g.canvas.width
- for (let i = -10; i <= 10; i++) {
- strokeInfiniteLine(g, i, 0, Math.PI / 2)
- }
- for (let i = -9; i <= 9; i++) {
- strokeInfiniteLine(g, 0, i, 0)
- }
+ g.strokeStyle = '#ddd'
+ g.lineWidth = 20 / width
+ for (let i = -10; i <= 10; i++) {
+ strokeInfiniteLine(g, i, 0, Math.PI / 2)
+ }
+ for (let i = -9; i <= 9; i++) {
+ strokeInfiniteLine(g, 0, i, 0)
+ }
- g.strokeStyle = '#333'
- g.lineWidth = 40 / g.canvas.width
- drawSimpleArrow(g, 0, 0, 9.8, 0, 0.35, '#444')
- drawSimpleArrow(g, 0, 0, 0, 9.8, 0.35, '#444')
+ g.strokeStyle = '#333'
+ g.lineWidth = 40 / width
+ drawSimpleArrow(g, 0, 0, 9.8, 0, 0.35, '#444')
+ drawSimpleArrow(g, 0, 0, 0, 9.8, 0.35, '#444')
+
+ // draw semiplanes
+
+ // draw semiplanes not in B
+ range(0, A.rows)
+ .filter(i => !B.includes(i))
+ .forEach(i => {
+ const [a1, a2] = A.rowAt(i).getData()
+ const b_i = b.at(i)
- // draw semiplanes
+ drawSemiplane(g, a1.toNumber(), a2.toNumber(), b_i.toNumber())
+ })
- // draw semiplanes not in B
- range(0, A.rows)
- .filter(i => !B.includes(i))
- .forEach(i => {
+ // draw semiplanes in B
+
+ B.forEach(i => {
const [a1, a2] = A.rowAt(i).getData()
const b_i = b.at(i)
- drawSemiplane(g, a1.toNumber(), a2.toNumber(), b_i.toNumber())
+ drawSemiplane(g, a1.toNumber(), a2.toNumber(), b_i.toNumber(), {
+ lineColor: '#040',
+ lineWidth: 3,
+ })
})
- // draw semiplanes in B
-
- B.forEach(i => {
- const [a1, a2] = A.rowAt(i).getData()
- const b_i = b.at(i)
+ // draw current solution
+ if (x) {
+ const [x1, x2] = x.getData()
- drawSemiplane(g, a1.toNumber(), a2.toNumber(), b_i.toNumber(), {
- lineColor: '#040',
- lineWidth: 3,
- })
- })
-
- // draw current solution
- if (x) {
- const [x1, x2] = x.getData()
-
- g.lineWidth = 50 / g.canvas.width
- drawSimpleArrow(
- g,
- x1.toNumber(),
- x2.toNumber(),
- x1.toNumber() + c1.toNumber() / cLen,
- x2.toNumber() + c2.toNumber() / cLen,
- 0.25,
- 'darkgreen'
- )
-
- // draw xi
- if (xi) {
- const [xi1, xi2] = xi.getData()
- const xiLen = Math.sqrt(xi1.toNumber() ** 2 + xi2.toNumber() ** 2)
-
- g.lineWidth = 50 / g.canvas.width
+ g.lineWidth = 50 / width
drawSimpleArrow(
g,
x1.toNumber(),
x2.toNumber(),
- x1.toNumber() + xi1.toNumber() / xiLen,
- x2.toNumber() + xi2.toNumber() / xiLen,
+ x1.toNumber() + c1.toNumber() / cLen,
+ x2.toNumber() + c2.toNumber() / cLen,
0.25,
- '#44d'
+ 'darkgreen'
)
- }
- g.fillStyle = '#d44'
- fillDot(g, x1.toNumber(), x2.toNumber(), 0.2)
- }
+ // draw xi
+ if (xi) {
+ const [xi1, xi2] = xi.getData()
+ const xiLen = Math.sqrt(xi1.toNumber() ** 2 + xi2.toNumber() ** 2)
+
+ g.lineWidth = 50 / width
+ drawSimpleArrow(
+ g,
+ x1.toNumber(),
+ x2.toNumber(),
+ x1.toNumber() + xi1.toNumber() / xiLen,
+ x2.toNumber() + xi2.toNumber() / xiLen,
+ 0.25,
+ '#44d'
+ )
+ }
- g.resetTransform()
+ g.fillStyle = '#d44'
+ fillDot(g, x1.toNumber(), x2.toNumber(), 0.2)
+ }
+ }
+ g.restore()
// draw c vector
diff --git a/src/lib-v2/canvas/index.ts b/src/lib-v2/canvas/index.ts
index 5a3d49b..3e6e989 100644
--- a/src/lib-v2/canvas/index.ts
+++ b/src/lib-v2/canvas/index.ts
@@ -69,7 +69,7 @@ export function drawSemiplane(
// Draw the line
g.strokeStyle = lineColor
- g.lineWidth = (lineWidth * 10) / g.canvas.width
+ g.lineWidth = (lineWidth * 10) / (g.canvas.width / window.devicePixelRatio)
g.beginPath()
if (a2 === 0) {
diff --git a/src/main.tsx b/src/main.tsx
index cf86138..17840bb 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -2,7 +2,7 @@ import { render } from 'preact'
import { useState } from 'preact/hooks'
import { parseSafeProblemInput } from './parser-problem'
import { DisplayProblemInput } from './DisplayProblemInput'
-import { Primale } from './Primale'
+import { Primal } from './Primal'
import exampleProblems from './example-problems.json'
@@ -43,6 +43,7 @@ const App = () => {
'ricerca-operativa.currentProblemName',
'Pintel'
)
+
const [savedProblems, setSavedProblems] = useLocalStorage<{ name: string; source: string }[]>(
'ricerca-operativa.savedProblems',
exampleProblems
@@ -137,7 +138,7 @@ const App = () => {
)}
- {'result' in problemValuesResult && }
+ {'result' in problemValuesResult && }
>
)
}