Skip to content

Depth aware sizing #321

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion web/src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState, useRef, useEffect } from 'react'
import useOnClickOutside from 'use-onclickoutside'
import { useRouteMatch } from 'react-router-dom'
import Zoom from '../Zoom/Zoom.connector'
import Zoom from '../Zoom/Zoom.connected'
import './Footer.scss'
import Icon from '../Icon/Icon'
import Button from '../Button/Button'
Expand All @@ -10,6 +10,7 @@ import MapViewingOptions from '../MapViewingOptions/MapViewingOptions'
import { AgentPubKeyB64, CellIdString } from '../../types/shared'
import { getAdminWs, getAppWs } from '../../hcWebsockets'
import SyncingIndicator from '../SyncingIndicator/SyncingIndicator'
import ZoomDepth from '../Zoom/ZoomDepth.connected'

export type FooterProps = {
agentAddress: AgentPubKeyB64
Expand Down Expand Up @@ -164,6 +165,7 @@ const Footer: React.FC<FooterProps> = ({
)}
{/* Map Zooming */}
{mapPage && <Zoom />}
{mapPage && <ZoomDepth />}
</div>
)}
</div>
Expand Down
90 changes: 37 additions & 53 deletions web/src/components/Zoom/Zoom.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,45 @@ import React from 'react'
import './Zoom.scss'
import Icon from '../Icon/Icon'

export type StateZoomProps = {
screensize: { width: number; height: number }
scale: number
}
// export type StateZoomProps = {
// screensize: { width: number; height: number }
// scale: number
// }

export type DispatchZoomProps = {
zoom: (
zoom: number,
pageCoord: { x: number; y: number },
instant?: boolean
) => void
}
// export type DispatchZoomProps = {
// zoom: (
// zoom: number,
// pageCoord: { x: number; y: number },
// instant?: boolean
// ) => void
// }

export type ZoomProps = StateZoomProps & DispatchZoomProps
export type ZoomProps = {
onClickPlus: () => void
onClickMinus: () => void
value: string
}

class Zoom extends React.Component<ZoomProps> {
constructor(props: ZoomProps) {
super(props)
this.zoomIn = this.zoomIn.bind(this)
this.zoomOut = this.zoomOut.bind(this)
}
zoomIn() {
const zoomIntensity = 0.05
const zoom = Math.exp(1 * zoomIntensity)
let { width, height } = this.props.screensize
const instant = true
this.props.zoom(zoom, { x: width / 2, y: height / 2 }, instant)
}
zoomOut() {
const zoomIntensity = 0.05
const zoom = Math.exp(-1 * zoomIntensity)
let { width, height } = this.props.screensize
const instant = true
this.props.zoom(zoom, { x: width / 2, y: height / 2 }, instant)
}
render() {
return (
<div className="zoom-wrapper">
<Icon
name="minus.svg"
size="small"
className="grey"
withBackground={false}
onClick={this.zoomOut}
/>
<Icon
name="plus.svg"
size="small"
className="grey"
withBackground={false}
onClick={this.zoomIn}
/>
<span>{Math.round(this.props.scale * 100)}%</span>
</div>
)
}
const Zoom: React.FC<ZoomProps> = ({ onClickPlus, onClickMinus, value }) => {
return (
<div className="zoom-wrapper">
<Icon
name="minus.svg"
size="small"
className="grey"
withBackground={false}
onClick={onClickMinus}
/>
<Icon
name="plus.svg"
size="small"
className="grey"
withBackground={false}
onClick={onClickPlus}
/>
<span>{value}</span>
</div>
)
}

export default Zoom
39 changes: 39 additions & 0 deletions web/src/components/Zoom/Zoom.connected.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { changeScale } from '../../redux/ephemeral/viewport/actions'
import { RootState } from '../../redux/reducer'
import Zoom from './Zoom.component'

const ConnectedZoom = () => {
const dispatch = useDispatch()
const scale = useSelector((state: RootState) => state.ui.viewport.scale)
const screensize = useSelector((state: RootState) => state.ui.screensize)
const value = `${Math.round(scale * 100)}%`
const zoom = (
zoom: number,
pageCoord: { x: number; y: number },
instant?: boolean
) => {
return dispatch(changeScale(zoom, pageCoord, instant))
}
const onClickPlus = () => {
const zoomIntensity = 0.05
const newZoom = Math.exp(1 * zoomIntensity)
let { width, height } = screensize
const instant = true
zoom(newZoom, { x: width / 2, y: height / 2 }, instant)
}
const onClickMinus = () => {
const zoomIntensity = 0.05
const newZoom = Math.exp(-1 * zoomIntensity)
let { width, height } = screensize
const instant = true
zoom(newZoom, { x: width / 2, y: height / 2 }, instant)
}

return (
<Zoom value={value} onClickPlus={onClickPlus} onClickMinus={onClickMinus} />
)
}

export default ConnectedZoom
21 changes: 0 additions & 21 deletions web/src/components/Zoom/Zoom.connector.ts

This file was deleted.

31 changes: 31 additions & 0 deletions web/src/components/Zoom/ZoomDepth.connected.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { RootState } from '../../redux/reducer'
import Zoom from './Zoom.component'
import { changeDepthPerception } from '../../redux/ephemeral/depth-perception/actions'

const ConnectedZoomDepth = () => {
const dispatch = useDispatch()
const depthPerception = useSelector(
(state: RootState) => state.ui.depthPerception.value
)
const changeP = (depthPerception: number) => {
return dispatch(changeDepthPerception(depthPerception))
}
const onClickPlus = () => {
changeP(depthPerception + 1)
}
const onClickMinus = () => {
changeP(depthPerception - 1)
}

return (
<Zoom
value={depthPerception.toString()}
onClickPlus={onClickPlus}
onClickMinus={onClickMinus}
/>
)
}

export default ConnectedZoomDepth
47 changes: 36 additions & 11 deletions web/src/drawing/dimensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,23 +283,48 @@ export function getOutcomeDimensions({

export function getOutcomeWidth({
outcome,
depthPerception = 0,
zoomLevel,
}: {
outcome: ComputedOutcome
zoomLevel: number
depthPerception?: number
zoomLevel?: number
}) {
// outcome width = outcome statement width + ( 2 * width padding)
const defaultWidth = 520 // 520 = 392 + ( 2 * 64 )

if (outcome.computedScope === ComputedScope.Small) {
// 0.02 < zoomLevel < 2.5
if (zoomLevel < 1) {
// 0.02 < zoomLevel < 1
return defaultWidth * Math.min(zoomLevel * 1.4, 1)
} else return defaultWidth
} else {
return defaultWidth
// 520 = 392 + ( 2 * 64 )
const defaultWidth = 520
const depth = outcome.depth || 0
// linear
const minWidth = 50
let depthSubtraction = 0
if (depthPerception === 1) {
if (depth === 1) {
depthSubtraction = 50
} else if (depth === 2) {
depthSubtraction = 100
} else if (depth === 3) {
depthSubtraction = 150
} else if (depth > 3) {
depthSubtraction = 200
}
} else if (depthPerception === 2) {

} else if (depthPerception === 3) {

}
return Math.max(minWidth, defaultWidth - depthSubtraction)
// logarithmic
// return defaultWidth * (1 / depth + 1)

// if (outcome.computedScope === ComputedScope.Small) {
// // 0.02 < zoomLevel < 2.5
// if (zoomLevel < 1) {
// // 0.02 < zoomLevel < 1
// return defaultWidth * Math.min(zoomLevel * 1.4, 1)
// } else return defaultWidth
// } else {
// return defaultWidth
// }
}

// height is a function of width
Expand Down
5 changes: 3 additions & 2 deletions web/src/drawing/layoutFormula.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ export default function layoutFormula(
[outcomeActionHash: string]: boolean
},
hiddenSmalls: boolean,
hiddenAchieved: boolean
hiddenAchieved: boolean,
depthPerception: number
): LayoutState {
let coordinates = {}
// just do this for efficiency, it's not going to
Expand All @@ -207,7 +208,7 @@ export default function layoutFormula(
Object.keys(graph.outcomes.computedOutcomesKeyed).forEach(
(outcomeActionHash) => {
const outcome = graph.outcomes.computedOutcomesKeyed[outcomeActionHash]
const width = getOutcomeWidth({ outcome, zoomLevel })
const width = getOutcomeWidth({ outcome, zoomLevel, depthPerception })
const height = getOutcomeHeight({
ctx,
outcome,
Expand Down
13 changes: 11 additions & 2 deletions web/src/event-listeners/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import {
setNavModalOpenChildren,
setNavModalOpenParents,
} from '../redux/ephemeral/navigation-modal/actions'
import { changeDepthPerception } from '../redux/ephemeral/depth-perception/actions'

// The "modifier" key is different on Mac and non-Mac
// Pattern borrowed from TinyKeys library.
Expand Down Expand Up @@ -433,18 +434,20 @@ export default function setupEventListeners(
}

function canvasWheel(event: WheelEvent) {
const state = store.getState()
const state: RootState = store.getState()
const {
ui: {
localPreferences: { navigation },
depthPerception: { value: depthPerception }
},
} = state
if (!state.ui.outcomeForm.isOpen) {
store.dispatch(unhoverOutcome())
store.dispatch(unsetContextMenu())
// from https://medium.com/@auchenberg/detecting-multi-touch-trackpad-gestures-in-javascript-a2505babb10e
// and https://stackoverflow.com/questions/2916081/zoom-in-on-a-point-using-scale-and-translate
if (navigation === MOUSE || (navigation === TRACKPAD && event.ctrlKey)) {
if (!event.metaKey && (navigation === MOUSE || (navigation === TRACKPAD && event.ctrlKey))) {
// NORMAL VIEWPORT ZOOMING
// Normalize wheel to +1 or -1.
const wheel = event.deltaY < 0 ? 1 : -1
const zoomIntensity = 0.07 // 0.05
Expand All @@ -453,7 +456,13 @@ export default function setupEventListeners(
const pageCoord = { x: event.clientX, y: event.clientY }
const instant = true
store.dispatch(changeScale(zoom, pageCoord, instant))
} else if (event.metaKey) {
// Normalize wheel to +1 or -1.
const wheel = event.deltaY < 0 ? -1 : 1
store.dispatch(changeDepthPerception(depthPerception + wheel))
console.log('depthPerception', depthPerception + wheel)
} else {
// NORMAL PANNING
// invert the pattern so that it uses new mac style
// of panning
store.dispatch(changeTranslate(-1 * event.deltaX, -1 * event.deltaY))
Expand Down
4 changes: 3 additions & 1 deletion web/src/redux/ephemeral/animations/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export default function performLayoutAnimation(
const graph = getGraphForState(nextState)
const zoomLevel = nextState.ui.viewport.scale
const translate = nextState.ui.viewport.translate
const depthPerception = nextState.ui.depthPerception.value
const projectId = nextState.ui.activeProject
const closestOutcome = nextState.ui.mouse.closestOutcome
const hiddenSmallOutcomes = nextState.ui.mapViewSettings.hiddenSmallOutcomes
Expand All @@ -81,7 +82,8 @@ export default function performLayoutAnimation(
projectTags,
collapsedOutcomes,
hiddenSmalls,
hiddenAchieved
hiddenAchieved,
depthPerception
)

// in terms of 'fixing' on a given outcome
Expand Down
5 changes: 4 additions & 1 deletion web/src/redux/ephemeral/animations/pan-and-zoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default function panZoomToFrame(
const projectTags = Object.values(state.projects.tags[activeProject] || {})
const hiddenSmallOutcomes = state.ui.mapViewSettings.hiddenSmallOutcomes
const hiddenAchievedOutcomes = state.ui.mapViewSettings.hiddenAchievedOutcomes
const depthPerception = state.ui.depthPerception.value
const hiddenSmalls = hiddenSmallOutcomes.includes(activeProject)
const hiddenAchieved = hiddenAchievedOutcomes.includes(activeProject)
const layeringAlgorithm =
Expand All @@ -60,7 +61,8 @@ export default function panZoomToFrame(
projectTags,
collapsedOutcomes,
hiddenSmalls,
hiddenAchieved
hiddenAchieved,
depthPerception,
)

// this accounts for a special case where the caller doesn't
Expand Down Expand Up @@ -100,6 +102,7 @@ export default function panZoomToFrame(
const outcomeWidth = getOutcomeWidth({
outcome,
zoomLevel, // use the target scale
depthPerception,
})
const outcomeHeight = getOutcomeHeight({
outcome,
Expand Down
Loading