Skip to content

Commit

Permalink
DoF for single lens and general sensor shape
Browse files Browse the repository at this point in the history
  • Loading branch information
monman53 committed Jul 3, 2024
1 parent 9232100 commit d81931b
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 61 deletions.
48 changes: 26 additions & 22 deletions src/SVG/Guideline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ import { vec, fGaussian } from '../math'
import WithBackground from './WithBackground.vue'
const x = computed(() => {
return globalLensRe.value.H
const xBack = computed(() => {
return globalLensRe.value.forward.H
})
const xFront = computed(() => {
return globalLensRe.value.backward.H
})
const f = computed(() => {
Expand All @@ -16,7 +20,7 @@ const f = computed(() => {
// Effective lens radius
const re = computed(() => {
return globalLensRe.value.re
return globalLensRe.value.forward.re
})
const sensorTop = computed(() => {
Expand All @@ -43,15 +47,15 @@ const sensorMiddle = computed(() => {
const focal = computed(() => {
const s = sensorTop.value
const t = sensorBottom.value
const ss = fGaussian(f.value, vec(x.value - s.x, s.y))
const tt = fGaussian(f.value, vec(x.value - t.x, t.y))
return { s: vec(x.value - ss.x, ss.y), t: vec(x.value - tt.x, tt.y) }
const ss = fGaussian(f.value, vec(xBack.value - s.x, s.y))
const tt = fGaussian(f.value, vec(xBack.value - t.x, t.y))
return { s: vec(xFront.value - ss.x, ss.y), t: vec(xFront.value - tt.x, tt.y) }
})
// Angle of view
const aov = computed(() => {
const lensTop = vec(x.value, -re.value)
const lensBottom = vec(x.value, re.value)
const lensTop = vec(xFront.value, -re.value)
const lensBottom = vec(xFront.value, re.value)
const focalPlaneTop = focal.value.t
const focalPlaneBottom = focal.value.s
Expand All @@ -62,13 +66,13 @@ const aov = computed(() => {
let middleInnerTop = focalPlaneBottom.sub(lensTop)
let middleInnerBottom = focalPlaneTop.sub(lensBottom)
// For over infinity modification
if (x.value + f.value > sensorMiddle.value.x) {
if (xBack.value + f.value > sensorMiddle.value.x) {
middleOuterTop = middleOuterTop.minus().normalize().mul(infR.value)
middleOuterBottom = middleOuterBottom.minus().normalize().mul(infR.value)
middleInnerTop = middleInnerTop.minus().normalize().mul(infR.value)
middleInnerBottom = middleInnerBottom.minus().normalize().mul(infR.value)
}
if (x.value + f.value === sensorMiddle.value.x) {
if (xBack.value + f.value === sensorMiddle.value.x) {
// middleOuterTop = vec(x.value - sensorMiddle.value.x, -sensor.value.r).normalize().mul(infR.value)
// middleInnerTop = vec(x.value - sensorMiddle.value.x, sensor.value.r).normalize().mul(infR.value)
}
Expand All @@ -89,7 +93,7 @@ const dof = computed(() => {
const sensorSize = st.length()
const c = sensor.value.circleOfConfusion
const v = st.normalize()
const s = sensor.value.s.add(vec(-x.value, 0))
const s = sensor.value.s.add(vec(-xBack.value, 0))
let inner = ""
let outer = ""
Expand All @@ -102,7 +106,7 @@ const dof = computed(() => {
const py = (a.y - r) / a.x * px + r
const p = fGaussian(f.value, vec(-px, py))
p.x = x.value - p.x
p.x = xFront.value - p.x
p.y = p.y
if (inner === "") {
inner += `M ${p.x} ${p.y} `
Expand All @@ -118,7 +122,7 @@ const dof = computed(() => {
const py = (a.y - r) / a.x * px + r
const p = fGaussian(f.value, vec(-px, py))
p.x = x.value - p.x
p.x = xFront.value - p.x
p.y = p.y
if (outer === "") {
outer += `M ${p.x} ${p.y} `
Expand All @@ -136,23 +140,23 @@ const dof = computed(() => {
<g class="stroke-white">
<!-- Inside camera -->
<g class="thick">
<line :x1="sensorTop.x" :y1="sensorTop.y" :x2="x" :y2="-re"></line>
<line :x1="sensorBottom.x" :y1="sensorBottom.y" :x2="x" :y2="re"></line>
<line :x1="sensorTop.x" :y1="sensorTop.y" :x2="xBack" :y2="-re"></line>
<line :x1="sensorBottom.x" :y1="sensorBottom.y" :x2="xBack" :y2="re"></line>
</g>
<g v-if="x !== sensorMiddle.x">
<g v-if="xBack !== sensorMiddle.x">
<!-- Lens to focal plane (outer) -->
<g class="thick">
<line :x1="x" :y1="-re" :x2="x + aov.middleOuterTop.x" :y2="-re + aov.middleOuterTop.y"></line>
<line :x1="x" :y1="re" :x2="x + aov.middleOuterBottom.x" :y2="re + aov.middleOuterBottom.y"></line>
<line :x1="xFront" :y1="-re" :x2="xFront + aov.middleOuterTop.x" :y2="-re + aov.middleOuterTop.y"></line>
<line :x1="xFront" :y1="re" :x2="xFront + aov.middleOuterBottom.x" :y2="re + aov.middleOuterBottom.y"></line>
</g>
<!-- Lens to focal plane (inner) -->
<g class="thicker">
<line :x1="x" :y1="-re" :x2="x + aov.middleInnerTop.x" :y2="-re + aov.middleInnerTop.y"></line>
<line :x1="x" :y1="re" :x2="x + aov.middleInnerBottom.x" :y2="re + aov.middleInnerBottom.y"></line>
<line :x1="xFront" :y1="-re" :x2="xFront + aov.middleInnerTop.x" :y2="-re + aov.middleInnerTop.y"></line>
<line :x1="xFront" :y1="re" :x2="xFront + aov.middleInnerBottom.x" :y2="re + aov.middleInnerBottom.y"></line>
</g>
</g>
<!-- Non over infinity -->
<g v-if="x + f < sensorMiddle.x">
<g v-if="xBack + f < sensorMiddle.x">
<!-- Focal plane -->
<line :x1="focal.s.x" :y1="focal.s.y" :x2="focal.t.x" :y2="focal.t.y" class="thick"></line>
<!-- Focal plane to inf (outer) -->
Expand All @@ -170,7 +174,7 @@ const dof = computed(() => {
:y2="focal.s.y + aov.innerBottom.y"></line>
</g>
<!-- Depth of focus -->
<g class="thicker">
<g class="thicker fill-none">
<path :d="dof.inner"></path>
<path :d="dof.outer"></path>
</g>
Expand Down
14 changes: 10 additions & 4 deletions src/SVG/SVG.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, ref, onMounted } from 'vue'
import { state, lights, lensGroups, style, apple, options, infR, rUI, globalLensInfo, globalLensRe, lensExist } from '../globals'
import { state, lights, lensGroups, style, apple, options, infR, rUI, globalLensInfo, globalLensRe, lensExist, lensesSorted } from '../globals'
import * as h from '../handlers'
import { Light } from '../type'
import { vec } from '../math'
Expand Down Expand Up @@ -75,14 +75,20 @@ const strokeWidth = computed(() => {
<Body v-if="options.body"></Body>

<!-- Guidelines -->
<Guideline v-if="options.lens && options.sensor && options.angleOfView"></Guideline>
<Guideline v-if="options.lens && lensesSorted.length === 1 && options.sensor && options.angleOfView"></Guideline>

<!-- Global focal point -->
<g v-if="options.lensFocalPoints && lensExist">
<WithBackground>
<line :x1="globalLensRe.H" :y1="-globalLensRe.re" :x2="globalLensRe.H" :y2="globalLensRe.re" class="stroke-white thick"></line>
<g class="stroke-white thicker">
<line :x1="globalLensRe.forward.H" :y1="-globalLensRe.forward.re" :x2="globalLensRe.forward.H"
:y2="globalLensRe.forward.re"></line>
<line :x1="globalLensRe.backward.H" :y1="-globalLensRe.backward.re" :x2="globalLensRe.backward.H"
:y2="globalLensRe.backward.re"></line>
</g>
</WithBackground>
<Point :c="vec(globalLensRe.H + globalLensInfo.f, 0)"></Point>
<Point :c="vec(globalLensRe.forward.H + globalLensInfo.f, 0)"></Point>
<Point :c="vec(globalLensRe.backward.H - globalLensInfo.f, 0)"></Point>
</g>

<!-- Items -->
Expand Down
148 changes: 113 additions & 35 deletions src/globals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ref, computed } from "vue"
import { ref, computed, watch } from "vue"
import { Vec, calcLensFront, vec, calcLensR, calcLensBack, fGaussian } from "./math"
import { type Lens, type LensGroup, Light } from "./type"

Expand Down Expand Up @@ -62,15 +62,19 @@ export const lensGroups0 = (): LensGroup[] => {
}
export const lensGroups = ref(lensGroups0())

export const lensesSorted = computed(() => {
const res = lensGroups.value.reduce((acc: Lens[], cur: LensGroup) => { return acc.concat(cur.lenses) }, [])
res.sort((a, b) => {
const lensSort = (lenses: Lens[]) => {
lenses.sort((a, b) => {
if (options.value.lensIdeal) {
return (a.x1 + a.x2) / 2 - (b.x1 + b.x2) / 2
} else {
return a.x1 - b.x1
return a.x1 - b.x2
}
})
}

export const lensesSorted = computed(() => {
const res = lensGroups.value.reduce((acc: Lens[], cur: LensGroup) => { return acc.concat(cur.lenses) }, [])
lensSort(res)
return res
})

Expand Down Expand Up @@ -375,13 +379,19 @@ export const body = computed(() => {
})

export const calcLensInfo = (lenses: Lens[]) => {
// TODO: This is not good
// Infinity loop may occur since calcLensInfo is used in lenSort()
// Skip for single element
if (lenses.length > 0) {
lensSort(lenses)
}
const ll: { d: number, r: number, n: number, nn: number }[] = []
lenses.forEach((lens, idx) => {
// Left-side
{
let d = idx === 0 ? 0 : lens.x1 - lenses[idx - 1].x2
if (options.value.lensIdeal && idx !== 0) {
d = (lens.x1 + lens.x2) / 2 - (lenses[idx - 1].x1 + lenses[idx - 1].x2) / 2
let d = lens.x2 - lens.x1
if (options.value.lensIdeal) {
d = 0
}
const r = lens.R1
const n = 1
Expand All @@ -390,9 +400,12 @@ export const calcLensInfo = (lenses: Lens[]) => {
}
// Right-side
{
let d = lens.x2 - lens.x1
if (options.value.lensIdeal) {
d = 0
let d = 0
if (idx < lenses.length - 1) {
d = lenses[idx + 1].x1 - lens.x2
if (options.value.lensIdeal) {
d = (lenses[idx + 1].x1 + lenses[idx + 1].x2) / 2 - (lens.x1 + lens.x2) / 2
}
}
const r = lens.R2
const n = lens.n
Expand All @@ -407,17 +420,22 @@ export const calcLensInfo = (lenses: Lens[]) => {
ll.forEach((l, i) => {
if (i === 0) {
//ls[i] = Infinity
// lss[i] = 1 / (l.n / l.nn / ls[i] + 1 / l.r * (1 - l.n / l.nn))
lss[i] = 1 / (1 / l.r * (1 - l.n / l.nn))
f *= lss[i]
} else {
ls[i] = lss[i - 1] - ll[i].d
ls[i] = lss[i - 1] - ll[i - 1].d
lss[i] = 1 / (l.n / l.nn / ls[i] + 1 / l.r * (1 - l.n / l.nn))
f *= lss[i] / ls[i]
}
})
let H = 0
if (lenses.length > 0) {
H = lenses[lenses.length - 1].x2 + lss[lss.length - 1] - f
if (options.value.lensIdeal) {
H = (lenses[lenses.length - 1].x1 + lenses[lenses.length - 1].x2) / 2 + lss[lss.length - 1] - f
} else {
H = lenses[lenses.length - 1].x2 + lss[lss.length - 1] - f
}
}
return { f, H }
}
Expand All @@ -428,32 +446,28 @@ export const globalLensInfos = computed(() => {
})
})

export const globalLensRe = computed(() => {
const calcLensRe = (lenses: Lens[], apertures: { r: number, x: number }[]) => {
// Setup
// Lenses
const params: { f: number, r: number, x: number }[] = []
lensesSorted.value.forEach((lens, idx) => {
const f = globalLensInfos.value[idx].f
lenses.forEach((lens, idx) => {
const info = calcLensInfo([lens])
const f = info.f
const r = calcLensR(lens) * lens.aperture
let x = globalLensInfos.value[idx].H
if (options.value.lensIdeal) {
x = (lens.x1 + lens.x2) / 2
}
const x = options.value.lensIdeal ? (lens.x1 + lens.x2) / 2 : info.H
params.push({ f, r, x })

if (!options.value.lensIdeal) {
// TODO:
// params.push({ f: Infinity, x: lens.x1, r: calcLensR(lens) })
// params.push({ f: Infinity, x: lens.x2, r: calcLensR(lens) })
}
})
// Body
if (!options.value.lensIdeal) {
lensFronts.value.forEach((front, idx) => {
params.push({ f: Infinity, r: lensRs.value[idx], x: front })
})
lensBacks.value.forEach((back, idx) => {
params.push({ f: Infinity, r: lensRs.value[idx], x: back })
})
}
// Apertures
if (options.value.aperture) {
params.push({ f: Infinity, r: aperture.value.r, x: aperture.value.x })
}
apertures.forEach((aperture) => {
params.push({ f: Infinity, r: aperture.r, x: aperture.x })
})
params.sort((a, b) => { return a.x - b.x })

// Calculation
Expand All @@ -471,8 +485,14 @@ export const globalLensRe = computed(() => {
} else {
const xp = params[idx - 1].x
if (f === Infinity) {
ps.push(ps[idx - 1])
res.push((ps[idx - 1] - x) / (ps[idx - 1] - xp) * res[idx - 1])
// TODO: Buggy around
if (ps[idx - 1] === Infinity) {
ps.push(ps[idx - 1])
res.push(r)
} else {
ps.push(ps[idx - 1])
res.push((ps[idx - 1] - x) / (ps[idx - 1] - xp) * res[idx - 1])
}
if (r < Math.abs(res[idx])) {
re *= r / Math.abs(res[idx])
res[idx] = r
Expand All @@ -492,7 +512,44 @@ export const globalLensRe = computed(() => {
}
}
})
return { re: Math.abs(res[0]) * re, H: ps[ps.length - 1] - globalLensInfo.value.f }
// return { re: Math.abs(res[0]) * re, H: ps[ps.length - 1] - calcLensInfo(lenses).f }
return { re: Math.abs(res[0]) * re, H: calcLensInfo(lenses).H }
}

export const globalLensRe = computed(() => {
const fwdApertures = []
const bwdApertures = []
if (options.value.aperture) {
fwdApertures.push({ x: aperture.value.x, r: aperture.value.r })
bwdApertures.push({ x: -aperture.value.x, r: aperture.value.r })
}
const fwdLenses = lensesSorted.value.map((lens) => {
return {
x1: lens.x1,
x2: lens.x2,
R1: lens.R1,
R2: lens.R2,
r: lens.r,
aperture: lens.aperture,
n: lens.n
}
})
const bwdLenses = lensesSorted.value.map((lens) => {
return {
x1: -lens.x2,
x2: -lens.x1,
R1: -lens.R2,
R2: -lens.R1,
r: lens.r,
aperture: lens.aperture,
n: lens.n
}
})
// console.log(calcLensInfo(fwdLenses), calcLensInfo(bwdLenses))
const forward = calcLensRe(fwdLenses, fwdApertures)
const backward = calcLensRe(bwdLenses, bwdApertures)
backward.H *= -1
return { forward, backward }
})

export const globalLensInfo = computed(() => {
Expand All @@ -501,4 +558,25 @@ export const globalLensInfo = computed(() => {

export const lensExist = computed(() => {
return lensesSorted.value.length > 0
})
})

//================================
// Simple test
//================================

const test = () => {
const SK6 = 1.61375
const SF1 = 1.71736
const lenses = [
{ x1: 0, x2: 7.7, R1: 85.4, R2: -500, n: SK6, r: 10, aperture: 1 },
{ x1: 8.2, x2: 27.2, R1: 44.5, R2: 70, n: SK6, r: 10, aperture: 1 },
{ x1: 31.7, x2: 33.7, R1: -135, R2: 34.3, n: SF1, r: 10, aperture: 1 },
{ x1: 52.7, x2: 60.7, R1: 146, R2: -46.8, n: SK6, r: 10, aperture: 1 },
]
const info = calcLensInfo(lenses)
console.log(info.f, 99.78672652)
}

watch([options], () => {
test()
}, { deep: true })

0 comments on commit d81931b

Please sign in to comment.