Add constraint colour indications (#73)

This commit is contained in:
Kurt Hutten
2023-03-19 21:25:22 +11:00
committed by GitHub
parent 22c356a841
commit 1ac3713a79
3 changed files with 120 additions and 4 deletions

View File

@ -17,6 +17,7 @@ import { useStore } from '../useStore'
import { isOverlap, roundOff } from '../lib/utils'
import { Vector3, DoubleSide, Quaternion } from 'three'
import { useSetCursor } from '../hooks/useSetCursor'
import { getConstraintLevelFromSourceRange } from '../lang/std/sketchcombos'
function MovingSphere({
geo,
@ -416,25 +417,43 @@ function LineRender({
rotation: Rotation
position: Position
}) {
const { setHighlightRange } = useStore((s) => ({
const { setHighlightRange, guiMode, ast } = useStore((s) => ({
setHighlightRange: s.setHighlightRange,
guiMode: s.guiMode,
ast: s.ast,
}))
const onClick = useSetCursor(sourceRange)
// This reference will give us direct access to the mesh
const ref = useRef<BufferGeometry | undefined>() as any
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 (
<>
<mesh
quaternion={rotation}
position={position}
ref={ref}
onPointerOver={(event) => {
onPointerOver={(e) => {
setHover(true)
setHighlightRange(sourceRange)
}}
onPointerOut={(event) => {
onPointerOut={(e) => {
setHover(false)
setHighlightRange([0, 0])
}}
@ -442,7 +461,7 @@ function LineRender({
>
<primitive object={geo} />
<meshStandardMaterial
color={hovered ? 'hotpink' : forceHighlight ? 'skyblue' : 'orange'}
color={hovered ? 'hotpink' : forceHighlight ? 'skyblue' : baseColor}
/>
</mesh>
</>

View File

@ -6,6 +6,7 @@ import {
transformAstSketchLines,
transformSecondarySketchLinesTagFirst,
ConstraintType,
getConstraintLevelFromSourceRange,
} from './sketchcombos'
import { initPromise } from '../rust'
import { TooTip } from '../../useStore'
@ -429,3 +430,64 @@ function helperThing(
})?.modifiedAst
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)
})
})
})

View File

@ -1220,3 +1220,38 @@ function createLastSeg(isX: boolean): CallExpression {
function getArgLiteralVal(arg: Value): number {
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'
}