Add constraint colour indications (#73)
This commit is contained in:
@ -17,6 +17,7 @@ import { useStore } from '../useStore'
|
|||||||
import { isOverlap, roundOff } from '../lib/utils'
|
import { isOverlap, roundOff } from '../lib/utils'
|
||||||
import { Vector3, DoubleSide, Quaternion } from 'three'
|
import { Vector3, DoubleSide, Quaternion } from 'three'
|
||||||
import { useSetCursor } from '../hooks/useSetCursor'
|
import { useSetCursor } from '../hooks/useSetCursor'
|
||||||
|
import { getConstraintLevelFromSourceRange } from '../lang/std/sketchcombos'
|
||||||
|
|
||||||
function MovingSphere({
|
function MovingSphere({
|
||||||
geo,
|
geo,
|
||||||
@ -416,25 +417,43 @@ function LineRender({
|
|||||||
rotation: Rotation
|
rotation: Rotation
|
||||||
position: Position
|
position: Position
|
||||||
}) {
|
}) {
|
||||||
const { setHighlightRange } = useStore((s) => ({
|
const { setHighlightRange, guiMode, ast } = useStore((s) => ({
|
||||||
setHighlightRange: s.setHighlightRange,
|
setHighlightRange: s.setHighlightRange,
|
||||||
|
guiMode: s.guiMode,
|
||||||
|
ast: s.ast,
|
||||||
}))
|
}))
|
||||||
const onClick = useSetCursor(sourceRange)
|
const onClick = useSetCursor(sourceRange)
|
||||||
// This reference will give us direct access to the mesh
|
// This reference will give us direct access to the mesh
|
||||||
const ref = useRef<BufferGeometry | undefined>() as any
|
const ref = useRef<BufferGeometry | undefined>() as any
|
||||||
const [hovered, setHover] = useState(false)
|
const [hovered, setHover] = useState(false)
|
||||||
|
|
||||||
|
const [baseColor, setBaseColor] = useState('orange')
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ast || guiMode.mode !== 'sketch') {
|
||||||
|
setBaseColor('orange')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const level = getConstraintLevelFromSourceRange(sourceRange, ast)
|
||||||
|
if (level === 'free') {
|
||||||
|
setBaseColor('orange')
|
||||||
|
} else if (level === 'partial') {
|
||||||
|
setBaseColor('IndianRed')
|
||||||
|
} else if (level === 'full') {
|
||||||
|
setBaseColor('lightgreen')
|
||||||
|
}
|
||||||
|
}, [guiMode, ast, sourceRange])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<mesh
|
<mesh
|
||||||
quaternion={rotation}
|
quaternion={rotation}
|
||||||
position={position}
|
position={position}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
onPointerOver={(event) => {
|
onPointerOver={(e) => {
|
||||||
setHover(true)
|
setHover(true)
|
||||||
setHighlightRange(sourceRange)
|
setHighlightRange(sourceRange)
|
||||||
}}
|
}}
|
||||||
onPointerOut={(event) => {
|
onPointerOut={(e) => {
|
||||||
setHover(false)
|
setHover(false)
|
||||||
setHighlightRange([0, 0])
|
setHighlightRange([0, 0])
|
||||||
}}
|
}}
|
||||||
@ -442,7 +461,7 @@ function LineRender({
|
|||||||
>
|
>
|
||||||
<primitive object={geo} />
|
<primitive object={geo} />
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
color={hovered ? 'hotpink' : forceHighlight ? 'skyblue' : 'orange'}
|
color={hovered ? 'hotpink' : forceHighlight ? 'skyblue' : baseColor}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
</>
|
</>
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
transformAstSketchLines,
|
transformAstSketchLines,
|
||||||
transformSecondarySketchLinesTagFirst,
|
transformSecondarySketchLinesTagFirst,
|
||||||
ConstraintType,
|
ConstraintType,
|
||||||
|
getConstraintLevelFromSourceRange,
|
||||||
} from './sketchcombos'
|
} from './sketchcombos'
|
||||||
import { initPromise } from '../rust'
|
import { initPromise } from '../rust'
|
||||||
import { TooTip } from '../../useStore'
|
import { TooTip } from '../../useStore'
|
||||||
@ -429,3 +430,64 @@ function helperThing(
|
|||||||
})?.modifiedAst
|
})?.modifiedAst
|
||||||
return recast(newAst)
|
return recast(newAst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe('testing getConstraintLevelFromSourceRange', () => {
|
||||||
|
it('should devide up lines into free, partial and fully contrained', () => {
|
||||||
|
const code = `const baseLength = 3
|
||||||
|
const baseThick = 1
|
||||||
|
const armThick = 0.5
|
||||||
|
const totalHeight = 4
|
||||||
|
const armAngle = 60
|
||||||
|
const totalLength = 9.74
|
||||||
|
const yDatum = 0
|
||||||
|
|
||||||
|
const baseThickHalf = baseThick / 2
|
||||||
|
const halfHeight = totalHeight / 2
|
||||||
|
const halfArmAngle = armAngle / 2
|
||||||
|
|
||||||
|
const part001 = startSketchAt([-0.01, -0.05])
|
||||||
|
|> line([0.01, 0.94 + 0], %) // partial
|
||||||
|
|> xLine(3.03, %) // partial
|
||||||
|
|> angledLine({
|
||||||
|
angle: halfArmAngle,
|
||||||
|
length: 2.45,
|
||||||
|
tag: 'seg01bing'
|
||||||
|
}, %) // partial
|
||||||
|
|> xLine(4.4, %) // partial
|
||||||
|
|> yLine(-1, %) // partial
|
||||||
|
|> xLine(-4.2 + 0, %) // full
|
||||||
|
|> angledLine([segAng('seg01bing', %) + 180, 1.79], %) // partial
|
||||||
|
|> line([1.44, -0.74], %) // free
|
||||||
|
|> xLine(3.36, %) // partial
|
||||||
|
|> line([-1.49, 1.06], %) // free
|
||||||
|
|> xLine(-3.43 + 0, %) // full
|
||||||
|
|> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full
|
||||||
|
show(part001)`
|
||||||
|
const ast = abstractSyntaxTree(lexer(code))
|
||||||
|
const constraintLevels: ReturnType<
|
||||||
|
typeof getConstraintLevelFromSourceRange
|
||||||
|
>[] = ['full', 'partial', 'free']
|
||||||
|
constraintLevels.forEach((constraintLevel) => {
|
||||||
|
const recursivelySeachCommentsAndCheckConstraintLevel = (
|
||||||
|
str: string,
|
||||||
|
offset: number = 0
|
||||||
|
): null => {
|
||||||
|
const index = str.indexOf(`// ${constraintLevel}`, offset)
|
||||||
|
if (index === -1) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const offsetIndex = index - 7
|
||||||
|
const expectedConstraintLevel = getConstraintLevelFromSourceRange(
|
||||||
|
[offsetIndex, offsetIndex],
|
||||||
|
ast
|
||||||
|
)
|
||||||
|
expect(expectedConstraintLevel).toBe(constraintLevel)
|
||||||
|
return recursivelySeachCommentsAndCheckConstraintLevel(
|
||||||
|
str,
|
||||||
|
index + constraintLevel.length
|
||||||
|
)
|
||||||
|
}
|
||||||
|
recursivelySeachCommentsAndCheckConstraintLevel(code)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -1220,3 +1220,38 @@ function createLastSeg(isX: boolean): CallExpression {
|
|||||||
function getArgLiteralVal(arg: Value): number {
|
function getArgLiteralVal(arg: Value): number {
|
||||||
return arg?.type === 'Literal' ? Number(arg.value) : 0
|
return arg?.type === 'Literal' ? Number(arg.value) : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getConstraintLevelFromSourceRange(
|
||||||
|
cursorRange: Range,
|
||||||
|
ast: Program
|
||||||
|
): 'free' | 'partial' | 'full' {
|
||||||
|
const { node: sketchFnExp } = getNodeFromPath<CallExpression>(
|
||||||
|
ast,
|
||||||
|
getNodePathFromSourceRange(ast, cursorRange)
|
||||||
|
)
|
||||||
|
const name = sketchFnExp?.callee?.name as TooTip
|
||||||
|
if (!toolTips.includes(name)) return 'free'
|
||||||
|
|
||||||
|
const firstArg = getFirstArg(sketchFnExp)
|
||||||
|
|
||||||
|
// check if the function is fully constrained
|
||||||
|
if (Array.isArray(firstArg.val)) {
|
||||||
|
const [a, b] = firstArg.val
|
||||||
|
if (a?.type !== 'Literal' && b?.type !== 'Literal') return 'full'
|
||||||
|
} else {
|
||||||
|
if (firstArg.val?.type !== 'Literal') return 'full'
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the function has no constraints
|
||||||
|
const isTwoValFree =
|
||||||
|
Array.isArray(firstArg.val) &&
|
||||||
|
firstArg.val?.[0]?.type === 'Literal' &&
|
||||||
|
firstArg.val?.[1]?.type === 'Literal'
|
||||||
|
const isOneValFree =
|
||||||
|
!Array.isArray(firstArg.val) && firstArg.val?.type === 'Literal'
|
||||||
|
|
||||||
|
if (isTwoValFree) return 'free'
|
||||||
|
if (isOneValFree) return 'partial'
|
||||||
|
|
||||||
|
return 'partial'
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user