Naive, one-click implementation of tool

This commit is contained in:
Frank Noirot
2024-07-02 15:25:15 -04:00
parent f86a69f12a
commit 01975a5447
5 changed files with 253 additions and 16 deletions

View File

@ -71,6 +71,14 @@ export function Toolbar({
: send('Equip rectangle tool'),
{ enabled: !disableAllButtons, scopes: ['sketch'] }
)
useHotkeys(
'c',
() =>
state.matches('Sketch.Circle tool')
? send('CancelSketch')
: send('Equip Circle tool'),
{ enabled: !disableAllButtons, scopes: ['sketch'] }
)
useHotkeys(
's',
() =>
@ -321,6 +329,38 @@ export function Toolbar({
</Tooltip>
</ActionButton>
</li>
<li className="contents" key="circle-button">
<ActionButton
className={buttonClassName}
Element="button"
onClick={() =>
state.matches('Sketch.Circle tool')
? send('CancelSketch')
: send('Equip Circle tool')
}
aria-pressed={state.matches('Sketch.Circle tool')}
iconStart={{
icon: 'circle',
iconClassName,
bgClassName,
}}
disabled={
(!state.can('Equip Circle tool') &&
!state.matches('Sketch.Circle tool')) ||
disableAllButtons
}
title="Circle"
>
Circle
<Tooltip
delay={1250}
position="bottom"
className="!px-2 !text-xs"
>
Shortcut: C
</Tooltip>
</ActionButton>
</li>
</>
)}
{state.matches('Sketch.SketchIdle') &&

View File

@ -102,6 +102,7 @@ import {
} from 'lib/rectangleTool'
import { getThemeColorForThreeJs } from 'lib/theme'
import { err, trap } from 'lib/trap'
import { addCircleToSketchAst } from 'lib/circleTool'
type DraftSegment = 'line' | 'tangentialArcTo'
@ -687,11 +688,8 @@ export class SceneEntities {
})
}
setupDraftRectangle = async (
sketchPathToNode: PathToNode,
forward: [number, number, number],
up: [number, number, number],
sketchOrigin: [number, number, number],
rectangleOrigin: [x: number, y: number]
rectangleOrigin: [number, number],
{ sketchPathToNode, origin, zAxis, yAxis }: SketchDetails
) => {
let _ast = JSON.parse(JSON.stringify(kclManager.ast))
@ -728,9 +726,9 @@ export class SceneEntities {
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
sketchPathToNode,
forward,
up,
position: sketchOrigin,
forward: yAxis,
up: zAxis,
position: origin,
maybeModdedAst: _ast,
draftExpressionsIndices: { start: 0, end: 3 },
})
@ -838,6 +836,52 @@ export class SceneEntities {
},
})
}
setupCircleOriginListener = () => {
sceneInfra.setCallbacks({
onClick: (args) => {
const twoD = args.intersectionPoint?.twoD
if (!twoD) {
console.warn(`This click didn't have a 2D intersection`, args)
return
}
sceneInfra.modelingSend({
type: 'Add circle origin',
data: [twoD.x, twoD.y],
})
},
})
}
setupDraftCircle = async (
circleOrigin: [number, number],
{ sketchPathToNode, origin, zAxis, yAxis }: SketchDetails
) => {
const astWithCircle = await addCircleToSketchAst({
sourceAst: kclManager.ast,
sketchPathToNode,
circleOrigin,
})
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
sketchPathToNode,
forward: yAxis,
up: zAxis,
position: origin,
maybeModdedAst: astWithCircle,
draftExpressionsIndices: { start: 0, end: 3 },
})
// TODO: Move this into the onclick handler to get the real shit
// Update the primary AST and unequip the rectangle tool
await kclManager.executeAstMock(astWithCircle)
sceneInfra.modelingSend({ type: 'CancelSketch' })
const { programMemory } = await executeAst({
ast: astWithCircle,
useFakeExecutor: true,
engineCommandManager: this.engineCommandManager,
programMemoryOverride,
})
}
setupSketchIdleCallbacks = ({
pathToNode,
up,
@ -1141,6 +1185,9 @@ export class SceneEntities {
? orthoFactor
: perspScale(sceneInfra.camControls.camera, group)) /
sceneInfra._baseUnitMultiplier
console.log('segment type', type)
if (type === TANGENTIAL_ARC_TO_SEGMENT) {
return this.updateTangentialArcToSegment({
prevSegment: sgPaths[index - 1],

View File

@ -101,6 +101,16 @@ const CustomIconMap = {
/>
</svg>
),
circle: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M10 2.5C9.01509 2.5 8.03982 2.69399 7.12988 3.0709C6.21994 3.44781 5.39314 4.00026 4.6967 4.6967C4.00026 5.39314 3.44782 6.21993 3.07091 7.12987C2.694 8.03981 2.5 9.01508 2.5 10C2.5 10.9849 2.69399 11.9602 3.0709 12.8701C3.44781 13.7801 4.00026 14.6069 4.6967 15.3033C5.39314 15.9997 6.21993 16.5522 7.12987 16.9291C8.03982 17.306 9.01509 17.5 10 17.5C10.9849 17.5 11.9602 17.306 12.8701 16.9291C13.7801 16.5522 14.6069 15.9997 15.3033 15.3033C15.9997 14.6069 16.5522 13.7801 16.9291 12.8701C17.306 11.9602 17.5 10.9849 17.5 10C17.5 9.01509 17.306 8.03982 16.9291 7.12988C16.5522 6.21993 15.9997 5.39314 15.3033 4.6967C14.6069 4.00026 13.7801 3.44781 12.8701 3.0709C11.9602 2.69399 10.9849 2.5 10 2.5ZM6.7472 2.14702C7.77847 1.71986 8.88377 1.5 10 1.5C11.1162 1.5 12.2215 1.71986 13.2528 2.14702C14.2841 2.57419 15.2211 3.20029 16.0104 3.98959C16.7997 4.77889 17.4258 5.71592 17.853 6.74719C18.2801 7.77846 18.5 8.88377 18.5 10C18.5 11.1162 18.2801 12.2215 17.853 13.2528C17.4258 14.2841 16.7997 15.2211 16.0104 16.0104C15.2211 16.7997 14.2841 17.4258 13.2528 17.853C12.2215 18.2801 11.1162 18.5 10 18.5C8.88376 18.5 7.77846 18.2801 6.74719 17.853C5.71592 17.4258 4.77889 16.7997 3.98959 16.0104C3.20029 15.2211 2.57419 14.2841 2.14702 13.2528C1.71986 12.2215 1.5 11.1162 1.5 10C1.5 8.88376 1.71986 7.77845 2.14703 6.74719C2.57419 5.71592 3.2003 4.77889 3.9896 3.98959C4.7789 3.20029 5.71593 2.57419 6.7472 2.14702Z"
fill="currentColor"
/>
</svg>
),
clipboardCheckmark: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path

92
src/lib/circleTool.ts Normal file
View File

@ -0,0 +1,92 @@
import {
createArrayExpression,
createCallExpressionStdLib,
createLiteral,
createPipeExpression,
createPipeSubstitution,
createTagDeclarator,
findUniqueName,
} from 'lang/modifyAst'
import { roundOff } from './utils'
import {
PathToNode,
Program,
VariableDeclaration,
parse,
recast,
} from 'lang/wasm'
import { getNodeFromPath } from 'lang/queryAst'
import { trap } from './trap'
/**
* Hide away the working with the AST
*/
export async function addCircleToSketchAst({
sourceAst,
sketchPathToNode,
circleOrigin,
}: {
sourceAst: Program
sketchPathToNode?: PathToNode
circleOrigin?: [number, number]
}) {
let _ast = JSON.parse(JSON.stringify(sourceAst))
const _node1 = getNodeFromPath<VariableDeclaration>(
_ast,
sketchPathToNode || [],
'VariableDeclaration'
)
if (trap(_node1)) return Promise.reject(_node1)
const tag = findUniqueName(_ast, 'circle')
const _node2 = getNodeFromPath<VariableDeclaration>(
_ast,
sketchPathToNode || [],
'VariableDeclaration'
)
if (trap(_node2)) return Promise.reject(_node2)
const startSketchOn = _node2.node?.declarations
const startSketchOnInit = startSketchOn?.[0]?.init
startSketchOn[0].init = createPipeExpression([
startSketchOnInit,
...getCircleCallExpressions({
center: circleOrigin,
tag,
}),
])
const maybeModdedAst = parse(recast(_ast))
if (trap(maybeModdedAst)) return Promise.reject(maybeModdedAst)
return Promise.resolve(maybeModdedAst)
}
/**
* Returns AST expressions for this KCL code:
* const yo = startSketchOn('XY')
* |> startProfileAt([0, 0], %)
* |> circle([0, 0], 0, %) <- this line
*/
export function getCircleCallExpressions({
center = [0, 0],
radius = 10,
tag,
}: {
center?: [number, number]
radius?: number
tag: string
}) {
return [
createCallExpressionStdLib('circle', [
createArrayExpression([
createLiteral(roundOff(center[0])),
createLiteral(roundOff(center[1])),
]),
createLiteral(roundOff(radius)),
createPipeSubstitution(),
createTagDeclarator(tag),
]),
]
}

File diff suppressed because one or more lines are too long