feat(packages): ludic.photo - a camera's photographs: what the lens sees (the frustum and a line of sight over the ground and the trunks), a grade out of a hundred on size, framing, light and the moment weighted by how much subject there is, the frame's best subject and its tags, the roll with its order and pages, a buyer's price on the grade's curve, the best of each subject; the lens, the light and the market through ports; kept, full, deleted and sold as facts; the roll in its own save section
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
473552cc81
commit
24a35f1174
17 changed files with 975 additions and 0 deletions
80
packages/ludic.photo/grade.ludic
Normal file
80
packages/ludic.photo/grade.ludic
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# grade.ludic - a photograph out of a hundred, for its best subject: SIZE in the frame (40), FRAMING
|
||||
# (20), LIGHT (20) and THE MOMENT (20). A tag is a tag at any grade; the grade is what a frame is
|
||||
# worth, never whether it happened.
|
||||
# the share of the picture's height the subject stands in, 0..1
|
||||
function pht_size_frac(h: float, d: float) -> float {
|
||||
let span = 2.0 * d * Math.tan(PhotoLens.fov() * 0.5)
|
||||
if span < 0.01 { return 1.0 }
|
||||
return Math.clamp(h / span, 0.0, 1.0)
|
||||
}
|
||||
|
||||
# 0..40: a record shot under 4%, the band that pays a quarter to two thirds, cropped past 85%
|
||||
export function photo_score_size(frac: float) -> int {
|
||||
if frac < 0.04 { return int(frac / 0.04 * 6.0) }
|
||||
if frac < 0.25 { return int(6.0 + (frac - 0.04) / 0.21 * 28.0) }
|
||||
if frac < 0.70 { return 40 }
|
||||
if frac < 0.85 { return int(40.0 - (frac - 0.70) / 0.15 * 6.0) }
|
||||
return int(Math.max(34.0 - (frac - 0.85) / 0.15 * 22.0, 8.0))
|
||||
}
|
||||
|
||||
# 0..20 from how near a thirds point it sits; cut by an edge, a third of that
|
||||
export function photo_score_frame(sx: float, sy: float, frac: float) -> int {
|
||||
let m = frac * 0.5
|
||||
if sx < 0.0 or sx > 1.0 or sy < 0.0 or sy > 1.0 { return 0 }
|
||||
var best = 9.0
|
||||
for i in 0 .. 2 {
|
||||
for j in 0 .. 2 {
|
||||
let dx = sx - float(i + 1) / 3.0
|
||||
let dy = sy - float(j + 1) / 3.0
|
||||
best = Math.min(best, Math.sqrt(dx * dx + dy * dy))
|
||||
}
|
||||
}
|
||||
var v = int(Math.max(1.0 - best / 0.28, 0.0) * 20.0)
|
||||
if sx < m or sx > 1.0 - m or sy < m or sy > 1.0 - m { v = v / 3 }
|
||||
return v
|
||||
}
|
||||
|
||||
# The whole score for a subject `h` metres tall standing at (x, y, z), doing something worth
|
||||
# `moment` (0..20). Composition, light and behaviour are qualities OF a subject, so they are
|
||||
# weighted by how much of one there is: a deer three pixels wide in perfect light is not a photograph.
|
||||
export function photo_score(x: float, y: float, z: float, h: float, moment: int) -> int {
|
||||
let dx = x - PhotoLens.x()
|
||||
let dy = y + 0.6 - PhotoLens.y()
|
||||
let dz = z - PhotoLens.z()
|
||||
let d = Math.max(Math.sqrt(dx * dx + dy * dy + dz * dz), 0.01)
|
||||
let frac = pht_size_frac(h, d)
|
||||
if pht_comp == null { pht_blank() }
|
||||
var sx = 0.5
|
||||
var sy = 0.5
|
||||
if PhotoLens.project(x, y + h * 0.5, z) {
|
||||
sx = PhotoLens.sx()
|
||||
sy = PhotoLens.sy()
|
||||
}
|
||||
pht_comp[0] = float(photo_score_size(frac))
|
||||
pht_comp[1] = float(photo_score_frame(sx, sy, frac))
|
||||
if PhotoLens.steady() { pht_comp[1] = Math.min(pht_comp[1] + 5.0, 20.0) }
|
||||
pht_comp[2] = float(photo_score_light(x, z))
|
||||
pht_comp[3] = float(Math.clamp(moment, 0, 20))
|
||||
pht_comp_have = true
|
||||
let presence = Math.clamp(pht_comp[0] / 40.0 * 1.4, 0.0, 1.0)
|
||||
if PhotoRules.behaviour() {
|
||||
pht_comp[0] = pht_comp[0] * 0.5
|
||||
pht_comp[3] = pht_comp[3] * 2.0
|
||||
}
|
||||
return int(pht_comp[0] + (pht_comp[1] + pht_comp[2] + pht_comp[3]) * presence)
|
||||
}
|
||||
|
||||
# the one thing to do differently next time: the worst component, or nothing to fault
|
||||
export function photo_why_of_comp() -> int {
|
||||
var worst = PHOTO_WHY_SIZE
|
||||
var least = pht_comp[0] / 40.0
|
||||
for k in 1 .. 4 {
|
||||
let f = pht_comp[k] / 20.0
|
||||
if f < least {
|
||||
least = f
|
||||
worst = k
|
||||
}
|
||||
}
|
||||
if least > 0.8 { return PHOTO_WHY_FINE }
|
||||
return worst
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue