Skip to content

Commit

Permalink
Merge commit '48ea58b99aa20a5fff9d0af8bb71559fa478dae2'
Browse files Browse the repository at this point in the history
  • Loading branch information
monman53 committed Jul 3, 2024
2 parents 45f3ae4 + 48ea58b commit 7f50f42
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 287 deletions.
226 changes: 11 additions & 215 deletions src/Canvas.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
import { watch, onMounted, ref } from 'vue'
import { state, lights, lensGroups, sensor, sensorData, apple, options, style, infR, lensesSorted, lensRs, lensFs, body, lensFronts, aperture, lensBacks } from './globals'
import { Vec, vec, vecRad, getIntersectionLens, crossAngle, fGaussian, intersectionSS, intersectionX, intersectionY, calcLensNWavelength } from './math'
import { Vec, vec, vecRad } from './math'
import { Light } from './type'
import { rayTrace } from './rayTrace';
// Reference to the canvas
const canvas = ref()
Expand All @@ -18,228 +19,23 @@ if (!ctx) {
throw new Error()
}
const drawSegment = (p: Vec, v: Vec, length: number) => {
const q = p.add(v.normalize().mul(length))
const drawSegment = (p: Vec, q: Vec) => {
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(q.x, q.y);
ctx.stroke();
return q
};
const intersectionBody = (s: Vec, v: Vec) => {
v = v.normalize()
let ps = []
// Outlines
if (options.value.body && body.value.r && body.value.front && body.value.back) {
ps.push(intersectionX(s, v, -body.value.r, body.value.front, body.value.back))
ps.push(intersectionX(s, v, body.value.r, body.value.front, body.value.back))
if (options.value.sensor) {
ps.push(intersectionY(s, v, body.value.back, -body.value.r, body.value.r))
}
}
// Lenses
if (options.value.lens) {
lensesSorted.value.forEach((lens, idx) => {
const lensR = lensRs.value[idx]
const front = lensFronts.value[idx]
const back = lensBacks.value[idx]
const xm = (lens.x1 + lens.x2) / 2
// Lens to body
if (options.value.body && body.value.r) {
if (options.value.lensIdeal) {
ps.push(intersectionY(s, v, xm, -body.value.r, -lensR))
ps.push(intersectionY(s, v, xm, lensR, body.value.r))
} else {
ps.push(intersectionY(s, v, front, -body.value.r, -lensR))
ps.push(intersectionY(s, v, front, lensR, body.value.r))
}
}
// Upper / Bottom
if (!options.value.lensIdeal) {
ps.push(intersectionX(s, v, -lensR, front, back))
ps.push(intersectionX(s, v, lensR, front, back))
}
// Aperture
ps.push(intersectionY(s, v, xm, -lensR, -lensR * lens.aperture))
ps.push(intersectionY(s, v, xm, lensR * lens.aperture, lensR))
})
}
// Aperture
if (options.value.aperture && body.value.r) {
ps.push(intersectionY(s, v, aperture.value.x, -body.value.r, -aperture.value.r))
ps.push(intersectionY(s, v, aperture.value.x, aperture.value.r, body.value.r))
}
ps = ps.filter((p) => p.p !== null)
ps.sort((p, q) => p.d - q.d)
if (ps.length === 0) {
return { p: null, d: null }
} else {
return ps[0]
}
}
const drawRay = (s: Vec, v: Vec, color: number, sensorDataTmp: any[]) => {
// Multiple lens
for (let lensIdx = 0; lensIdx < lensesSorted.value.length; lensIdx++) {
const lens = lensesSorted.value[lensIdx]
const s0 = s.copy()
const xm = (lens.x1 + lens.x2) / 2
const f = lensFs.value[lensIdx]
const r = lensRs.value[lensIdx]
const n = options.value.wavelength ? calcLensNWavelength(lens.n, color) : lens.n
let innerLens = false
//--------------------------------
// Collision to lens surface (left-side)
//--------------------------------
if (!options.value.lensIdeal && options.value.lens) {
// Center of lens curvature circle
const c = vec(lens.x1 + lens.R1, 0)
let pl = getIntersectionLens(s, v, c, r, lens.R1)
const pb = intersectionBody(s, v)
if ((pb.p && pl.p && pb.d < pl.d) || (!pl.p && pb.p)) {
v = pb.p.sub(s)
drawSegment(s, v, v.length())
return
}
if (pl.p) {
const p = pl.p
v = p.sub(s)
s = drawSegment(s, v, v.length())
// Refraction (inner lens rays)
const phi1 = crossAngle(Vec.sub(p, c).mul(lens.R1 > 0 ? 1 : -1), Vec.sub(s0, p));
const phi2 = Math.asin(Math.sin(phi1) / n);
const theta = Math.atan2(p.y - c.y, p.x - c.x) + Math.PI + phi2;
if (lens.R1 > 0) {
v = vecRad(theta)
} else {
v = vecRad(theta + Math.PI)
}
innerLens = true
}
// Optimization
if (lensIdx === 0 && !pl.p && !pb.p) {
drawSegment(s, v, infR.value)
return
}
}
//--------------------------------
// Collision to lens surface (right-side)
//--------------------------------
if (!options.value.lensIdeal && innerLens) {
// Center of lens curvature circle
const c = vec(lens.x2 + lens.R2, 0)
const pl = getIntersectionLens(s, v, c, r, lens.R2)
const pb = intersectionBody(s, v)
if ((pb.p && pl.p && pb.d < pl.d) || (!pl.p && pb.p)) {
v = pb.p.sub(s)
drawSegment(s, v, v.length())
return
}
if (pl.p) {
const p = pl.p
v = p.sub(s)
const nextS = drawSegment(s, v, v.length())
// Refraction (inner lens rays)
const phi1 = crossAngle(Vec.sub(p, c).mul(lens.R2 < 0 ? 1 : -1), Vec.sub(p, s));
const phi2 = Math.asin(Math.sin(phi1) * n);
const theta = Math.atan2(p.y - c.y, p.x - c.x) + phi2;
if (lens.R2 > 0) {
v = vecRad(theta + Math.PI)
} else {
v = vecRad(theta)
}
s = nextS
}
}
//--------------------------------
// Collision to ideal lens
//--------------------------------
if (options.value.lensIdeal && options.value.lens) {
const pl = intersectionY(s, v, xm, -r, r)
const pb = intersectionBody(s, v)
// TODO: Find better condition
const eps = 1e-9
if ((pb.p && pl.p && pb.d < pl.d + eps) || (!pl.p && pb.p)) {
v = pb.p.sub(s)
drawSegment(s, v, v.length())
return
}
if (pl.p) {
const p = pl.p
v = p.sub(s)
s = drawSegment(s, v, v.length())
// Find image position of the light source
// TODO:
const image = fGaussian(f, vec(s0.x - xm, s0.y))
// Refracted ray
let theta = Math.atan2(image.y - s.y, image.x);
if (image.x === Infinity) {
theta = Math.atan2(-s0.y, -(s0.x - xm));
}
if (f < 0) {
theta += Math.PI
}
if (xm - f < s0.x) {
theta += Math.PI
}
v = vecRad(theta)
}
// Optimization
if (lensIdx === 0 && !pl.p && !pb.p) {
drawSegment(s, v, infR.value)
return
}
}
}
//--------------------------------
// Collision to sensor
//--------------------------------
if (options.value.sensor) {
const ps = intersectionSS(s, s.add(v.mul(infR.value)), sensor.value.s, sensor.value.t)
const pb = intersectionBody(s, v)
if ((pb.p && ps && pb.d < (ps.sub(s).length())) || (!ps && pb.p)) {
v = pb.p.sub(s)
drawSegment(s, v, v.length())
const ps = rayTrace(s.copy(), v.copy())
ps.forEach((p) => {
drawSegment(s, p.p)
if (p.isSensor) {
sensorDataTmp.push({ y: p.p.sub(sensor.value.s).length(), color })
return
}
if (ps) {
const p = ps
v = p.sub(s)
drawSegment(s, v, v.length())
sensorDataTmp.push({ y: p.sub(sensor.value.s).length(), color })
return
}
}
const pb = intersectionBody(s, v)
if (pb.p) {
v = pb.p.sub(s)
drawSegment(s, v, v.length())
return
}
// to infinity
drawSegment(s, v, infR.value)
s = p.p
})
return
}
Expand All @@ -255,7 +51,7 @@ const draw = () => {
const sensorDataTmp: any[] = []
//================================
// Light path drawing like ray-tracing
// Light path drawing
//================================
// Light sources
Expand Down
6 changes: 2 additions & 4 deletions src/SVG/Aperture.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { aperture, body, maxLightX, sensor, style } from '@/globals';
import { aperture, body, sensor, style } from '@/globals';
import WithBackground from './WithBackground.vue';
import CircleUI from './CircleUI.vue';
import { Vec, vec } from '@/math';
Expand All @@ -23,9 +23,7 @@ const move = () => {
return (e: any, d: Vec) => {
let xn = x0 + d.x
const sensorMinX = Math.min(sensor.value.s.x, sensor.value.t.x)
if (xn < maxLightX.value) {
xn = maxLightX.value
} else if (xn > sensorMinX) {
if (xn > sensorMinX) {
xn = sensorMinX
}
aperture.value.x = xn
Expand Down
5 changes: 1 addition & 4 deletions src/SVG/Body.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { aperture, body, lensesSorted, lensFronts, lensGroups, lensRs, maxLightX, options, sensor } from '@/globals';
import { aperture, body, lensesSorted, lensFronts, lensGroups, lensRs, options, sensor } from '@/globals';
import WithBackground from './WithBackground.vue';
import * as h from '../handlers'
Expand All @@ -19,9 +19,6 @@ const move = (e: any) => {
minX = Math.min(minX, x1)
})
])
if (minX + d.x < maxLightX.value) {
d.x = maxLightX.value - minX
}
// Update
lensGroups.value.forEach((lensGroup, i) => {
Expand Down
9 changes: 2 additions & 7 deletions src/SVG/Lens.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { computed } from 'vue'
import { sensor, options, maxLightX, calcLensInfo } from '../globals'
import { sensor, options, calcLensInfo } from '../globals'
import { vec, calcRMax, calcLensFront, calcLensBack } from '../math'
import { setMoveHandler, preventDefaultAndStopPropagation, getPositionOnSvg, getPositionDiffOnSvgApp } from '../handlers'
import type { Lens } from '../type'
Expand Down Expand Up @@ -82,9 +82,7 @@ const x1MoveStartHandler = (e: any) => {
const x10 = lens.x1;
setMoveHandler((e_: any) => {
const d = getPositionDiffOnSvgApp(e_, m0)
if (x10 + d.x < maxLightX.value) {
lens.x1 = maxLightX.value
} else if (x10 + d.x > props.lens.x2) {
if (x10 + d.x > props.lens.x2) {
lens.x1 = props.lens.x2
} else {
lens.x1 = x10 + d.x
Expand Down Expand Up @@ -120,9 +118,6 @@ const r1MoveStartHandler = (e: any) => {
setMoveHandler((e_: any) => {
const d = getPositionDiffOnSvgApp(e_, m0)
let x1n = x10 + d.x
if (x1n < maxLightX.value) {
x1n = maxLightX.value
}
if (x1n > props.lens.x2) {
x1n = props.lens.x2
}
Expand Down
8 changes: 2 additions & 6 deletions src/SVG/LensGroup.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { lensGroups, releaseAllLenses, sensor, maxLightX } from '../globals'
import { lensGroups, releaseAllLenses, sensor } from '../globals'
import { setMoveHandler, preventDefaultAndStopPropagation, getPositionOnSvg, getPositionDiffOnSvgApp } from '../handlers'
import { type LensGroup } from '../type'
Expand Down Expand Up @@ -32,11 +32,7 @@ const moveStartHandler = (e: any) => {
}
for (let j = 0; j < lensGroup.lenses.length; j++) {
const sensorMinX = Math.min(sensor.value.s.x, sensor.value.t.x)
if (x10s[i][j] + d.x < maxLightX.value) {
if (Math.abs(maxLightX.value - x10s[i][j]) < Math.abs(d.x)) {
d.x = maxLightX.value - x10s[i][j]
}
} else if (x20s[i][j] + d.x > sensorMinX) {
if (x20s[i][j] + d.x > sensorMinX) {
if (Math.abs(sensorMinX - x20s[i][j]) < Math.abs(d.x)) {
d.x = sensorMinX - x20s[i][j]
}
Expand Down
18 changes: 2 additions & 16 deletions src/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,15 +207,15 @@ export const options0 = () => {
return {
advanced: false,
lens: true,
lensIdeal: true,
lensIdeal: false,
lensFocalPoints: false,
lensDoubleFocalPoints: false,
hyperfocalPoint: false,
sensor: true,
sensorPreview: true,
sensorMemory: false,
body: true,
aperture: true,
aperture: false,
grid: false,
opticalAxis: false,
curvature: false,
Expand Down Expand Up @@ -295,20 +295,6 @@ export const maxLensX = computed(() => {
return maxX
})

export const maxLightX = computed(() => {
let max = -infR.value
for (const light of lights.value) {
if (light.type === Light.Point) {
max = Math.max(max, light.c.x)
}
if (light.type === Light.Parallel) {
max = Math.max(max, light.s.x)
max = Math.max(max, light.t.x)
}
}
return max
})

export const rUI = computed(() => {
const scale = 1 / state.value.scale
return 8 * scale;
Expand Down
8 changes: 4 additions & 4 deletions src/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ export const lightMoveStartHandler = (e: any, idx: number) => {
const c0 = light.c.copy()
moveHandler = (e_: any) => {
const d = getPositionDiffOnSvgApp(e_, m0)
if (c0.x + d.x > minLensX.value) {
light.c.x = minLensX.value
} else {
// if (c0.x + d.x > minLensX.value) {
// light.c.x = minLensX.value
// } else {
light.c.x = c0.x + d.x
}
// }
light.c.y = c0.y + d.y
}
}
Expand Down
Loading

0 comments on commit 7f50f42

Please sign in to comment.