Naive, one-click implementation of tool
This commit is contained in:
@ -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') &&
|
||||
|
||||
@ -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],
|
||||
|
||||
@ -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
92
src/lib/circleTool.ts
Normal 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
Reference in New Issue
Block a user