Refactor clientSide scene (#3859)
* refactor clientSide scene * start consolidate threejs segment funcitons * rename stuff * first pass of integrating threejs segment create and update into one * reduce create segment complexity * add color back in * use input * fix comment * feedback changes
This commit is contained in:
@ -34,13 +34,11 @@ import { CustomIcon, CustomIconName } from 'components/CustomIcon'
|
||||
import { ConstrainInfo } from 'lang/std/stdTypes'
|
||||
import { getConstraintInfo } from 'lang/std/sketch'
|
||||
import { Dialog, Popover, Transition } from '@headlessui/react'
|
||||
import { LineInputsType } from 'lang/std/sketchcombos'
|
||||
import toast from 'react-hot-toast'
|
||||
import { InstanceProps, create } from 'react-modal-promise'
|
||||
import { executeAst } from 'lang/langHelpers'
|
||||
import {
|
||||
deleteSegmentFromPipeExpression,
|
||||
makeRemoveSingleConstraintInput,
|
||||
removeSingleConstraintInfo,
|
||||
} from 'lang/modifyAst'
|
||||
import { ActionButton } from 'components/ActionButton'
|
||||
@ -542,12 +540,10 @@ const ConstraintSymbol = ({
|
||||
iconName: 'dimension',
|
||||
},
|
||||
}
|
||||
const varName =
|
||||
_type in varNameMap ? varNameMap[_type as LineInputsType].varName : 'var'
|
||||
const name: CustomIconName = varNameMap[_type as LineInputsType].iconName
|
||||
const displayName = varNameMap[_type as LineInputsType]?.displayName
|
||||
const implicitDesc =
|
||||
varNameMap[_type as LineInputsType]?.implicitConstraintDesc
|
||||
const varName = varNameMap?.[_type]?.varName || 'var'
|
||||
const name: CustomIconName = varNameMap[_type].iconName
|
||||
const displayName = varNameMap[_type]?.displayName
|
||||
const implicitDesc = varNameMap[_type]?.implicitConstraintDesc
|
||||
|
||||
const _node = useMemo(
|
||||
() => getNodeFromPath<Expr>(kclManager.ast, pathToNode),
|
||||
@ -604,13 +600,10 @@ const ConstraintSymbol = ({
|
||||
if (trap(_node1)) return Promise.reject(_node1)
|
||||
const shallowPath = _node1.shallowPath
|
||||
|
||||
const input = makeRemoveSingleConstraintInput(
|
||||
argPosition,
|
||||
shallowPath
|
||||
)
|
||||
if (!input || !context.sketchDetails) return
|
||||
if (!context.sketchDetails || !argPosition) return
|
||||
const transform = removeSingleConstraintInfo(
|
||||
input,
|
||||
shallowPath,
|
||||
argPosition,
|
||||
kclManager.ast,
|
||||
kclManager.programMemory
|
||||
)
|
||||
|
@ -1,10 +1,8 @@
|
||||
import {
|
||||
BoxGeometry,
|
||||
DoubleSide,
|
||||
ExtrudeGeometry,
|
||||
Group,
|
||||
Intersection,
|
||||
LineCurve3,
|
||||
Mesh,
|
||||
MeshBasicMaterial,
|
||||
Object3D,
|
||||
@ -15,7 +13,6 @@ import {
|
||||
Points,
|
||||
Quaternion,
|
||||
Scene,
|
||||
Shape,
|
||||
Vector2,
|
||||
Vector3,
|
||||
} from 'three'
|
||||
@ -27,8 +24,6 @@ import {
|
||||
OnClickCallbackArgs,
|
||||
OnMouseEnterLeaveArgs,
|
||||
RAYCASTABLE_PLANE,
|
||||
SEGMENT_LENGTH_LABEL,
|
||||
SEGMENT_LENGTH_LABEL_TEXT,
|
||||
SKETCH_GROUP_SEGMENTS,
|
||||
SKETCH_LAYER,
|
||||
X_AXIS,
|
||||
@ -37,7 +32,6 @@ import {
|
||||
import { isQuaternionVertical, quaternionFromUpNForward } from './helpers'
|
||||
import {
|
||||
CallExpression,
|
||||
getTangentialArcToInfo,
|
||||
parse,
|
||||
Path,
|
||||
PathToNode,
|
||||
@ -61,11 +55,9 @@ import {
|
||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||
import { executeAst } from 'lang/langHelpers'
|
||||
import {
|
||||
createArcGeometry,
|
||||
dashedStraight,
|
||||
profileStart,
|
||||
straightSegment,
|
||||
tangentialArcToSegment,
|
||||
createProfileStartHandle,
|
||||
SegmentUtils,
|
||||
segmentUtils,
|
||||
} from './segments'
|
||||
import {
|
||||
addCallExpressionsToPipe,
|
||||
@ -74,13 +66,7 @@ import {
|
||||
changeSketchArguments,
|
||||
updateStartProfileAtArgs,
|
||||
} from 'lang/std/sketch'
|
||||
import {
|
||||
isArray,
|
||||
isOverlap,
|
||||
normaliseAngle,
|
||||
roundOff,
|
||||
throttle,
|
||||
} from 'lib/utils'
|
||||
import { isArray, isOverlap, roundOff } from 'lib/utils'
|
||||
import {
|
||||
addStartProfileAt,
|
||||
createArrayExpression,
|
||||
@ -91,7 +77,6 @@ import {
|
||||
findUniqueName,
|
||||
} from 'lang/modifyAst'
|
||||
import { Selections, getEventForSegmentSelection } from 'lib/selections'
|
||||
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
||||
import { createGridHelper, orthoScale, perspScale } from './helpers'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
@ -121,6 +106,11 @@ export const TANGENTIAL_ARC_TO_SEGMENT_BODY = 'tangential-arc-to-segment-body'
|
||||
export const SEGMENT_WIDTH_PX = 1.6
|
||||
export const HIDE_SEGMENT_LENGTH = 75 // in pixels
|
||||
export const HIDE_HOVER_SEGMENT_LENGTH = 60 // in pixels
|
||||
export const SEGMENT_BODIES = [STRAIGHT_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT]
|
||||
export const SEGMENT_BODIES_PLUS_PROFILE_START = [
|
||||
...SEGMENT_BODIES,
|
||||
PROFILE_START,
|
||||
]
|
||||
|
||||
type Vec3Array = [number, number, number]
|
||||
|
||||
@ -154,37 +144,35 @@ export class SceneEntities {
|
||||
? orthoFactor
|
||||
: perspScale(sceneInfra.camControls.camera, segment)) /
|
||||
sceneInfra._baseUnitMultiplier
|
||||
const input = {
|
||||
type: 'straight-segment',
|
||||
from: segment.userData.from,
|
||||
to: segment.userData.to,
|
||||
} as const
|
||||
let update: SegmentUtils['update'] | null = null
|
||||
if (
|
||||
segment.userData.from &&
|
||||
segment.userData.to &&
|
||||
segment.userData.type === STRAIGHT_SEGMENT
|
||||
) {
|
||||
callbacks.push(
|
||||
this.updateStraightSegment({
|
||||
from: segment.userData.from,
|
||||
to: segment.userData.to,
|
||||
group: segment,
|
||||
scale: factor,
|
||||
})
|
||||
)
|
||||
update = segmentUtils.straight.update
|
||||
}
|
||||
|
||||
if (
|
||||
segment.userData.from &&
|
||||
segment.userData.to &&
|
||||
segment.userData.prevSegment &&
|
||||
segment.userData.type === TANGENTIAL_ARC_TO_SEGMENT
|
||||
) {
|
||||
callbacks.push(
|
||||
this.updateTangentialArcToSegment({
|
||||
update = segmentUtils.tangentialArcTo.update
|
||||
}
|
||||
const callBack = update?.({
|
||||
prevSegment: segment.userData.prevSegment,
|
||||
from: segment.userData.from,
|
||||
to: segment.userData.to,
|
||||
input,
|
||||
group: segment,
|
||||
scale: factor,
|
||||
sceneInfra,
|
||||
})
|
||||
)
|
||||
}
|
||||
callBack && !err(callBack) && callbacks.push(callBack)
|
||||
if (segment.name === PROFILE_START) {
|
||||
segment.scale.set(factor, factor, factor)
|
||||
}
|
||||
@ -421,7 +409,7 @@ export class SceneEntities {
|
||||
maybeModdedAst,
|
||||
sketchGroup.start.__geoMeta.sourceRange
|
||||
)
|
||||
const _profileStart = profileStart({
|
||||
const _profileStart = createProfileStartHandle({
|
||||
from: sketchGroup.start.from,
|
||||
id: sketchGroup.start.__geoMeta.id,
|
||||
pathToNode: segPathToNode,
|
||||
@ -476,50 +464,31 @@ export class SceneEntities {
|
||||
if (err(_node1)) return
|
||||
const callExpName = _node1.node?.callee?.name
|
||||
|
||||
if (segment.type === 'TangentialArcTo') {
|
||||
seg = tangentialArcToSegment({
|
||||
const initSegment =
|
||||
segment.type === 'TangentialArcTo'
|
||||
? segmentUtils.tangentialArcTo.init
|
||||
: segmentUtils.straight.init
|
||||
const result = initSegment({
|
||||
prevSegment: sketchGroup.value[index - 1],
|
||||
from: segment.from,
|
||||
to: segment.to,
|
||||
id: segment.__geoMeta.id,
|
||||
pathToNode: segPathToNode,
|
||||
isDraftSegment,
|
||||
scale: factor,
|
||||
texture: sceneInfra.extraSegmentTexture,
|
||||
theme: sceneInfra._theme,
|
||||
isSelected,
|
||||
})
|
||||
callbacks.push(
|
||||
this.updateTangentialArcToSegment({
|
||||
prevSegment: sketchGroup.value[index - 1],
|
||||
from: segment.from,
|
||||
to: segment.to,
|
||||
group: seg,
|
||||
scale: factor,
|
||||
})
|
||||
)
|
||||
} else {
|
||||
seg = straightSegment({
|
||||
from: segment.from,
|
||||
to: segment.to,
|
||||
id: segment.__geoMeta.id,
|
||||
pathToNode: segPathToNode,
|
||||
isDraftSegment,
|
||||
scale: factor,
|
||||
callExpName,
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
from: segment.from,
|
||||
to: segment.to,
|
||||
},
|
||||
id: segment.__geoMeta.id,
|
||||
pathToNode: segPathToNode,
|
||||
isDraftSegment,
|
||||
scale: factor,
|
||||
texture: sceneInfra.extraSegmentTexture,
|
||||
theme: sceneInfra._theme,
|
||||
isSelected,
|
||||
sceneInfra,
|
||||
})
|
||||
callbacks.push(
|
||||
this.updateStraightSegment({
|
||||
from: segment.from,
|
||||
to: segment.to,
|
||||
group: seg,
|
||||
scale: factor,
|
||||
})
|
||||
)
|
||||
}
|
||||
if (err(result)) return
|
||||
const { group: _group, updateOverlaysCallback } = result
|
||||
seg = _group
|
||||
callbacks.push(updateOverlaysCallback)
|
||||
seg.layers.set(SKETCH_LAYER)
|
||||
seg.traverse((child) => {
|
||||
child.layers.set(SKETCH_LAYER)
|
||||
@ -602,16 +571,19 @@ export class SceneEntities {
|
||||
kclManager.programMemory.get(variableDeclarationName),
|
||||
variableDeclarationName
|
||||
)
|
||||
if (err(sg)) return sg
|
||||
const lastSeg = sg.value?.slice(-1)[0] || sg.start
|
||||
if (err(sg)) return Promise.reject(sg)
|
||||
const lastSeg = sg?.value?.slice(-1)[0] || sg.start
|
||||
|
||||
const index = sg.value.length // because we've added a new segment that's not in the memory yet, no need for `-1`
|
||||
|
||||
const mod = addNewSketchLn({
|
||||
node: _ast,
|
||||
programMemory: kclManager.programMemory,
|
||||
to: [lastSeg.to[0], lastSeg.to[1]],
|
||||
from: [lastSeg.to[0], lastSeg.to[1]],
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
to: lastSeg.to,
|
||||
from: lastSeg.to,
|
||||
},
|
||||
fnName: segmentName,
|
||||
pathToNode: sketchPathToNode,
|
||||
})
|
||||
@ -682,8 +654,11 @@ export class SceneEntities {
|
||||
const tmp = addNewSketchLn({
|
||||
node: kclManager.ast,
|
||||
programMemory: kclManager.programMemory,
|
||||
to: [intersection2d.x, intersection2d.y],
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
from: [lastSegment.to[0], lastSegment.to[1]],
|
||||
to: [intersection2d.x, intersection2d.y],
|
||||
},
|
||||
fnName:
|
||||
lastSegment.type === 'TangentialArcTo'
|
||||
? 'tangentialArcTo'
|
||||
@ -834,14 +809,14 @@ export class SceneEntities {
|
||||
sketchPathToNode || [],
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (trap(_node)) return Promise.reject(_node)
|
||||
if (trap(_node)) return
|
||||
const sketchInit = _node.node?.declarations?.[0]?.init
|
||||
|
||||
if (sketchInit.type === 'PipeExpression') {
|
||||
updateRectangleSketch(sketchInit, x, y, tags[0])
|
||||
|
||||
let _recastAst = parse(recast(_ast))
|
||||
if (trap(_recastAst)) return Promise.reject(_recastAst)
|
||||
if (trap(_recastAst)) return
|
||||
_ast = _recastAst
|
||||
|
||||
// Update the primary AST and unequip the rectangle tool
|
||||
@ -861,7 +836,7 @@ export class SceneEntities {
|
||||
programMemory.get(variableDeclarationName),
|
||||
variableDeclarationName
|
||||
)
|
||||
if (err(sketchGroup)) return sketchGroup
|
||||
if (err(sketchGroup)) return
|
||||
const sgPaths = sketchGroup.value
|
||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||
|
||||
@ -950,8 +925,11 @@ export class SceneEntities {
|
||||
const mod = addNewSketchLn({
|
||||
node: kclManager.ast,
|
||||
programMemory: kclManager.programMemory,
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
to: [intersectionPoint.twoD.x, intersectionPoint.twoD.y],
|
||||
from: [prevSegment.from[0], prevSegment.from[1]],
|
||||
from: prevSegment.from,
|
||||
},
|
||||
// TODO assuming it's always a straight segments being added
|
||||
// as this is easiest, and we'll need to add "tabbing" behavior
|
||||
// to support other segment types
|
||||
@ -1072,7 +1050,7 @@ export class SceneEntities {
|
||||
group.userData.from[0],
|
||||
group.userData.from[1],
|
||||
]
|
||||
const to: [number, number] = [intersection2d.x, intersection2d.y]
|
||||
const dragTo: [number, number] = [intersection2d.x, intersection2d.y]
|
||||
let modifiedAst = draftInfo ? draftInfo.truncatedAst : { ...kclManager.ast }
|
||||
|
||||
const _node = getNodeFromPath<CallExpression>(
|
||||
@ -1095,8 +1073,11 @@ export class SceneEntities {
|
||||
modded = updateStartProfileAtArgs({
|
||||
node: modifiedAst,
|
||||
pathToNode,
|
||||
to,
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
to: dragTo,
|
||||
from,
|
||||
},
|
||||
previousProgramMemory: kclManager.programMemory,
|
||||
})
|
||||
} else {
|
||||
@ -1104,8 +1085,11 @@ export class SceneEntities {
|
||||
modifiedAst,
|
||||
kclManager.programMemory,
|
||||
[node.start, node.end],
|
||||
to,
|
||||
from
|
||||
{
|
||||
type: 'straight-segment',
|
||||
from,
|
||||
to: dragTo,
|
||||
}
|
||||
)
|
||||
}
|
||||
if (trap(modded)) return
|
||||
@ -1208,264 +1192,36 @@ export class SceneEntities {
|
||||
? orthoFactor
|
||||
: perspScale(sceneInfra.camControls.camera, group)) /
|
||||
sceneInfra._baseUnitMultiplier
|
||||
const input = {
|
||||
type: 'straight-segment',
|
||||
from: segment.from,
|
||||
to: segment.to,
|
||||
} as const
|
||||
let update: SegmentUtils['update'] | null = null
|
||||
if (type === TANGENTIAL_ARC_TO_SEGMENT) {
|
||||
return this.updateTangentialArcToSegment({
|
||||
prevSegment: sgPaths[index - 1],
|
||||
from: segment.from,
|
||||
to: segment.to,
|
||||
group: group,
|
||||
scale: factor,
|
||||
})
|
||||
update = segmentUtils.tangentialArcTo.update
|
||||
} else if (type === STRAIGHT_SEGMENT) {
|
||||
return this.updateStraightSegment({
|
||||
from: segment.from,
|
||||
to: segment.to,
|
||||
update = segmentUtils.straight.update
|
||||
}
|
||||
const callBack =
|
||||
update &&
|
||||
!err(update) &&
|
||||
update({
|
||||
input,
|
||||
group,
|
||||
scale: factor,
|
||||
prevSegment: sgPaths[index - 1],
|
||||
sceneInfra,
|
||||
})
|
||||
} else if (type === PROFILE_START) {
|
||||
if (callBack && !err(callBack)) return callBack
|
||||
|
||||
if (type === PROFILE_START) {
|
||||
group.position.set(segment.from[0], segment.from[1], 0)
|
||||
group.scale.set(factor, factor, factor)
|
||||
}
|
||||
return () => null
|
||||
}
|
||||
|
||||
updateTangentialArcToSegment({
|
||||
prevSegment,
|
||||
from,
|
||||
to,
|
||||
group,
|
||||
scale = 1,
|
||||
}: {
|
||||
prevSegment: SketchGroup['value'][number]
|
||||
from: [number, number]
|
||||
to: [number, number]
|
||||
group: Group
|
||||
scale?: number
|
||||
}): () => SegmentOverlayPayload | null {
|
||||
group.userData.from = from
|
||||
group.userData.to = to
|
||||
group.userData.prevSegment = prevSegment
|
||||
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
|
||||
const extraSegmentGroup = group.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
||||
|
||||
const previousPoint =
|
||||
prevSegment?.type === 'TangentialArcTo'
|
||||
? getTangentPointFromPreviousArc(
|
||||
prevSegment.center,
|
||||
prevSegment.ccw,
|
||||
prevSegment.to
|
||||
)
|
||||
: prevSegment.from
|
||||
|
||||
const arcInfo = getTangentialArcToInfo({
|
||||
arcStartPoint: from,
|
||||
arcEndPoint: to,
|
||||
tanPreviousPoint: previousPoint,
|
||||
obtuse: true,
|
||||
})
|
||||
|
||||
const pxLength = arcInfo.arcLength / scale
|
||||
const shouldHideIdle = pxLength < HIDE_SEGMENT_LENGTH
|
||||
const shouldHideHover = pxLength < HIDE_HOVER_SEGMENT_LENGTH
|
||||
|
||||
const hoveredParent =
|
||||
sceneInfra.hoveredObject &&
|
||||
getParentGroup(sceneInfra.hoveredObject, [TANGENTIAL_ARC_TO_SEGMENT])
|
||||
let isHandlesVisible = !shouldHideIdle
|
||||
if (hoveredParent && hoveredParent?.uuid === group?.uuid) {
|
||||
isHandlesVisible = !shouldHideHover
|
||||
}
|
||||
|
||||
if (arrowGroup) {
|
||||
arrowGroup.position.set(to[0], to[1], 0)
|
||||
|
||||
const arrowheadAngle =
|
||||
arcInfo.endAngle + (Math.PI / 2) * (arcInfo.ccw ? 1 : -1)
|
||||
arrowGroup.quaternion.setFromUnitVectors(
|
||||
new Vector3(0, 1, 0),
|
||||
new Vector3(Math.cos(arrowheadAngle), Math.sin(arrowheadAngle), 0)
|
||||
)
|
||||
arrowGroup.scale.set(scale, scale, scale)
|
||||
arrowGroup.visible = isHandlesVisible
|
||||
}
|
||||
|
||||
if (extraSegmentGroup) {
|
||||
const circumferenceInPx = (2 * Math.PI * arcInfo.radius) / scale
|
||||
const extraSegmentAngleDelta =
|
||||
(EXTRA_SEGMENT_OFFSET_PX / circumferenceInPx) * Math.PI * 2
|
||||
const extraSegmentAngle =
|
||||
arcInfo.startAngle + (arcInfo.ccw ? 1 : -1) * extraSegmentAngleDelta
|
||||
const extraSegmentOffset = new Vector2(
|
||||
Math.cos(extraSegmentAngle) * arcInfo.radius,
|
||||
Math.sin(extraSegmentAngle) * arcInfo.radius
|
||||
)
|
||||
extraSegmentGroup.position.set(
|
||||
arcInfo.center[0] + extraSegmentOffset.x,
|
||||
arcInfo.center[1] + extraSegmentOffset.y,
|
||||
0
|
||||
)
|
||||
extraSegmentGroup.scale.set(scale, scale, scale)
|
||||
extraSegmentGroup.visible = isHandlesVisible
|
||||
}
|
||||
|
||||
const tangentialArcToSegmentBody = group.children.find(
|
||||
(child) => child.userData.type === TANGENTIAL_ARC_TO_SEGMENT_BODY
|
||||
) as Mesh
|
||||
|
||||
if (tangentialArcToSegmentBody) {
|
||||
const newGeo = createArcGeometry({ ...arcInfo, scale })
|
||||
tangentialArcToSegmentBody.geometry = newGeo
|
||||
}
|
||||
const tangentialArcToSegmentBodyDashed = group.children.find(
|
||||
(child) => child.userData.type === TANGENTIAL_ARC_TO__SEGMENT_DASH
|
||||
) as Mesh
|
||||
if (tangentialArcToSegmentBodyDashed) {
|
||||
// consider throttling the whole updateTangentialArcToSegment
|
||||
// if there are more perf considerations going forward
|
||||
this.throttledUpdateDashedArcGeo({
|
||||
...arcInfo,
|
||||
mesh: tangentialArcToSegmentBodyDashed,
|
||||
isDashed: true,
|
||||
scale,
|
||||
})
|
||||
}
|
||||
const angle = normaliseAngle(
|
||||
(arcInfo.endAngle * 180) / Math.PI + (arcInfo.ccw ? 90 : -90)
|
||||
)
|
||||
return () =>
|
||||
sceneInfra.updateOverlayDetails({
|
||||
arrowGroup,
|
||||
group,
|
||||
isHandlesVisible,
|
||||
from,
|
||||
to,
|
||||
angle,
|
||||
})
|
||||
}
|
||||
throttledUpdateDashedArcGeo = throttle(
|
||||
(
|
||||
args: Parameters<typeof createArcGeometry>[0] & {
|
||||
mesh: Mesh
|
||||
scale: number
|
||||
}
|
||||
) => (args.mesh.geometry = createArcGeometry(args)),
|
||||
1000 / 30
|
||||
)
|
||||
updateStraightSegment({
|
||||
from,
|
||||
to,
|
||||
group,
|
||||
scale = 1,
|
||||
}: {
|
||||
from: [number, number]
|
||||
to: [number, number]
|
||||
group: Group
|
||||
scale?: number
|
||||
}): () => SegmentOverlayPayload | null {
|
||||
group.userData.from = from
|
||||
group.userData.to = to
|
||||
const shape = new Shape()
|
||||
shape.moveTo(0, (-SEGMENT_WIDTH_PX / 2) * scale) // The width of the line in px (2.4px in this case)
|
||||
shape.lineTo(0, (SEGMENT_WIDTH_PX / 2) * scale)
|
||||
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
|
||||
const labelGroup = group.getObjectByName(SEGMENT_LENGTH_LABEL) as Group
|
||||
|
||||
const length = Math.sqrt(
|
||||
Math.pow(to[0] - from[0], 2) + Math.pow(to[1] - from[1], 2)
|
||||
)
|
||||
|
||||
const pxLength = length / scale
|
||||
const shouldHideIdle = pxLength < HIDE_SEGMENT_LENGTH
|
||||
const shouldHideHover = pxLength < HIDE_HOVER_SEGMENT_LENGTH
|
||||
|
||||
const hoveredParent =
|
||||
sceneInfra.hoveredObject &&
|
||||
getParentGroup(sceneInfra.hoveredObject, [STRAIGHT_SEGMENT])
|
||||
let isHandlesVisible = !shouldHideIdle
|
||||
if (hoveredParent && hoveredParent?.uuid === group?.uuid) {
|
||||
isHandlesVisible = !shouldHideHover
|
||||
}
|
||||
|
||||
if (arrowGroup) {
|
||||
arrowGroup.position.set(to[0], to[1], 0)
|
||||
|
||||
const dir = new Vector3()
|
||||
.subVectors(
|
||||
new Vector3(to[0], to[1], 0),
|
||||
new Vector3(from[0], from[1], 0)
|
||||
)
|
||||
.normalize()
|
||||
arrowGroup.quaternion.setFromUnitVectors(new Vector3(0, 1, 0), dir)
|
||||
arrowGroup.scale.set(scale, scale, scale)
|
||||
arrowGroup.visible = isHandlesVisible
|
||||
}
|
||||
|
||||
const extraSegmentGroup = group.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
||||
if (extraSegmentGroup) {
|
||||
const offsetFromBase = new Vector2(to[0] - from[0], to[1] - from[1])
|
||||
.normalize()
|
||||
.multiplyScalar(EXTRA_SEGMENT_OFFSET_PX * scale)
|
||||
extraSegmentGroup.position.set(
|
||||
from[0] + offsetFromBase.x,
|
||||
from[1] + offsetFromBase.y,
|
||||
0
|
||||
)
|
||||
extraSegmentGroup.scale.set(scale, scale, scale)
|
||||
extraSegmentGroup.visible = isHandlesVisible
|
||||
}
|
||||
|
||||
if (labelGroup) {
|
||||
const labelWrapper = labelGroup.getObjectByName(
|
||||
SEGMENT_LENGTH_LABEL_TEXT
|
||||
) as CSS2DObject
|
||||
const labelWrapperElem = labelWrapper.element as HTMLDivElement
|
||||
const label = labelWrapperElem.children[0] as HTMLParagraphElement
|
||||
label.innerText = `${roundOff(length)}`
|
||||
label.classList.add(SEGMENT_LENGTH_LABEL_TEXT)
|
||||
const slope = (to[1] - from[1]) / (to[0] - from[0])
|
||||
let slopeAngle = ((Math.atan(slope) * 180) / Math.PI) * -1
|
||||
label.style.setProperty('--degree', `${slopeAngle}deg`)
|
||||
label.style.setProperty('--x', `0px`)
|
||||
label.style.setProperty('--y', `0px`)
|
||||
labelWrapper.position.set((from[0] + to[0]) / 2, (from[1] + to[1]) / 2, 0)
|
||||
labelGroup.visible = isHandlesVisible
|
||||
}
|
||||
|
||||
const straightSegmentBody = group.children.find(
|
||||
(child) => child.userData.type === STRAIGHT_SEGMENT_BODY
|
||||
) as Mesh
|
||||
if (straightSegmentBody) {
|
||||
const line = new LineCurve3(
|
||||
new Vector3(from[0], from[1], 0),
|
||||
new Vector3(to[0], to[1], 0)
|
||||
)
|
||||
straightSegmentBody.geometry = new ExtrudeGeometry(shape, {
|
||||
steps: 2,
|
||||
bevelEnabled: false,
|
||||
extrudePath: line,
|
||||
})
|
||||
}
|
||||
const straightSegmentBodyDashed = group.children.find(
|
||||
(child) => child.userData.type === STRAIGHT_SEGMENT_DASH
|
||||
) as Mesh
|
||||
if (straightSegmentBodyDashed) {
|
||||
straightSegmentBodyDashed.geometry = dashedStraight(
|
||||
from,
|
||||
to,
|
||||
shape,
|
||||
scale
|
||||
)
|
||||
}
|
||||
return () =>
|
||||
sceneInfra.updateOverlayDetails({
|
||||
arrowGroup,
|
||||
group,
|
||||
isHandlesVisible,
|
||||
from,
|
||||
to,
|
||||
})
|
||||
}
|
||||
/**
|
||||
* Update the base color of each of the THREEjs meshes
|
||||
* that represent each of the sketch segments, to get the
|
||||
@ -1578,27 +1334,30 @@ export class SceneEntities {
|
||||
}
|
||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||
|
||||
const input = {
|
||||
type: 'straight-segment',
|
||||
from: parent.userData.from,
|
||||
to: parent.userData.to,
|
||||
} as const
|
||||
const factor =
|
||||
(sceneInfra.camControls.camera instanceof OrthographicCamera
|
||||
? orthoFactor
|
||||
: perspScale(sceneInfra.camControls.camera, parent)) /
|
||||
sceneInfra._baseUnitMultiplier
|
||||
let update: SegmentUtils['update'] | null = null
|
||||
if (parent.name === STRAIGHT_SEGMENT) {
|
||||
this.updateStraightSegment({
|
||||
from: parent.userData.from,
|
||||
to: parent.userData.to,
|
||||
group: parent,
|
||||
scale: factor,
|
||||
})
|
||||
update = segmentUtils.straight.update
|
||||
} else if (parent.name === TANGENTIAL_ARC_TO_SEGMENT) {
|
||||
this.updateTangentialArcToSegment({
|
||||
update = segmentUtils.tangentialArcTo.update
|
||||
}
|
||||
update &&
|
||||
update({
|
||||
prevSegment: parent.userData.prevSegment,
|
||||
from: parent.userData.from,
|
||||
to: parent.userData.to,
|
||||
input,
|
||||
group: parent,
|
||||
scale: factor,
|
||||
sceneInfra,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
editorManager.setHighlightRange([[0, 0]])
|
||||
@ -1613,28 +1372,31 @@ export class SceneEntities {
|
||||
if (parent) {
|
||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||
|
||||
const input = {
|
||||
type: 'straight-segment',
|
||||
from: parent.userData.from,
|
||||
to: parent.userData.to,
|
||||
} as const
|
||||
const factor =
|
||||
(sceneInfra.camControls.camera instanceof OrthographicCamera
|
||||
? orthoFactor
|
||||
: perspScale(sceneInfra.camControls.camera, parent)) /
|
||||
sceneInfra._baseUnitMultiplier
|
||||
let update: SegmentUtils['update'] | null = null
|
||||
if (parent.name === STRAIGHT_SEGMENT) {
|
||||
this.updateStraightSegment({
|
||||
from: parent.userData.from,
|
||||
to: parent.userData.to,
|
||||
group: parent,
|
||||
scale: factor,
|
||||
})
|
||||
update = segmentUtils.straight.update
|
||||
} else if (parent.name === TANGENTIAL_ARC_TO_SEGMENT) {
|
||||
this.updateTangentialArcToSegment({
|
||||
update = segmentUtils.tangentialArcTo.update
|
||||
}
|
||||
update &&
|
||||
update({
|
||||
prevSegment: parent.userData.prevSegment,
|
||||
from: parent.userData.from,
|
||||
to: parent.userData.to,
|
||||
input,
|
||||
group: parent,
|
||||
scale: factor,
|
||||
sceneInfra,
|
||||
})
|
||||
}
|
||||
}
|
||||
const isSelected = parent?.userData?.isSelected
|
||||
colorSegment(
|
||||
selected,
|
||||
|
@ -26,6 +26,7 @@ import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm'
|
||||
import {
|
||||
EXTRA_SEGMENT_HANDLE,
|
||||
EXTRA_SEGMENT_OFFSET_PX,
|
||||
HIDE_HOVER_SEGMENT_LENGTH,
|
||||
HIDE_SEGMENT_LENGTH,
|
||||
PROFILE_START,
|
||||
SEGMENT_WIDTH_PX,
|
||||
@ -35,18 +36,448 @@ import {
|
||||
TANGENTIAL_ARC_TO_SEGMENT,
|
||||
TANGENTIAL_ARC_TO_SEGMENT_BODY,
|
||||
TANGENTIAL_ARC_TO__SEGMENT_DASH,
|
||||
getParentGroup,
|
||||
} from './sceneEntities'
|
||||
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
||||
import {
|
||||
ARROWHEAD,
|
||||
SceneInfra,
|
||||
SEGMENT_LENGTH_LABEL,
|
||||
SEGMENT_LENGTH_LABEL_OFFSET_PX,
|
||||
SEGMENT_LENGTH_LABEL_TEXT,
|
||||
} from './sceneInfra'
|
||||
import { Themes, getThemeColorForThreeJs } from 'lib/theme'
|
||||
import { roundOff } from 'lib/utils'
|
||||
import { normaliseAngle, roundOff } from 'lib/utils'
|
||||
import { SegmentOverlayPayload } from 'machines/modelingMachine'
|
||||
import { SegmentInputs } from 'lang/std/stdTypes'
|
||||
import { err } from 'lib/trap'
|
||||
|
||||
export function profileStart({
|
||||
interface CreateSegmentArgs {
|
||||
input: SegmentInputs
|
||||
prevSegment: SketchGroup['value'][number]
|
||||
id: string
|
||||
pathToNode: PathToNode
|
||||
isDraftSegment?: boolean
|
||||
scale?: number
|
||||
callExpName: string
|
||||
texture: Texture
|
||||
theme: Themes
|
||||
isSelected?: boolean
|
||||
sceneInfra: SceneInfra
|
||||
}
|
||||
|
||||
interface UpdateSegmentArgs {
|
||||
input: SegmentInputs
|
||||
prevSegment: SketchGroup['value'][number]
|
||||
group: Group
|
||||
sceneInfra: SceneInfra
|
||||
scale?: number
|
||||
}
|
||||
|
||||
interface CreateSegmentResult {
|
||||
group: Group
|
||||
updateOverlaysCallback: () => SegmentOverlayPayload | null
|
||||
}
|
||||
|
||||
export interface SegmentUtils {
|
||||
/**
|
||||
* the init is responsible for adding all of the correct entities to the group with important details like `mesh.name = ...`
|
||||
* as these act like handles later
|
||||
*
|
||||
* It's **Not** responsible for doing all calculations to size and position the entities as this would be duplicated in the update function
|
||||
* Which should instead be called at the end of the init function
|
||||
*/
|
||||
init: (args: CreateSegmentArgs) => CreateSegmentResult | Error
|
||||
/**
|
||||
* The update function is responsible for updating the group with the correct size and position of the entities
|
||||
* It should be called at the end of the init function and return a callback that can be used to update the overlay
|
||||
*
|
||||
* It returns a callback for updating the overlays, this is so the overlays do not have to update at the same pace threeJs does
|
||||
* This is useful for performance reasons
|
||||
*/
|
||||
update: (
|
||||
args: UpdateSegmentArgs
|
||||
) => CreateSegmentResult['updateOverlaysCallback'] | Error
|
||||
}
|
||||
|
||||
class StraightSegment implements SegmentUtils {
|
||||
init: SegmentUtils['init'] = ({
|
||||
input,
|
||||
id,
|
||||
pathToNode,
|
||||
isDraftSegment,
|
||||
scale = 1,
|
||||
callExpName,
|
||||
texture,
|
||||
theme,
|
||||
isSelected = false,
|
||||
sceneInfra,
|
||||
prevSegment,
|
||||
}) => {
|
||||
if (input.type !== 'straight-segment')
|
||||
return new Error('Invalid segment type')
|
||||
const { from, to } = input
|
||||
const baseColor =
|
||||
callExpName === 'close' ? 0x444444 : getThemeColorForThreeJs(theme)
|
||||
const color = isSelected ? 0x0000ff : baseColor
|
||||
const meshType = isDraftSegment
|
||||
? STRAIGHT_SEGMENT_DASH
|
||||
: STRAIGHT_SEGMENT_BODY
|
||||
|
||||
const segmentGroup = new Group()
|
||||
const shape = new Shape()
|
||||
const line = new LineCurve3(
|
||||
new Vector3(from[0], from[1], 0),
|
||||
new Vector3(to[0], to[1], 0)
|
||||
)
|
||||
const geometry = new ExtrudeGeometry(shape, {
|
||||
steps: 2,
|
||||
bevelEnabled: false,
|
||||
extrudePath: line,
|
||||
})
|
||||
const body = new MeshBasicMaterial({ color })
|
||||
const mesh = new Mesh(geometry, body)
|
||||
|
||||
mesh.userData.type = meshType
|
||||
mesh.name = meshType
|
||||
segmentGroup.name = STRAIGHT_SEGMENT
|
||||
segmentGroup.userData = {
|
||||
type: STRAIGHT_SEGMENT,
|
||||
id,
|
||||
from,
|
||||
to,
|
||||
pathToNode,
|
||||
isSelected,
|
||||
callExpName,
|
||||
baseColor,
|
||||
}
|
||||
|
||||
// All segment types get an extra segment handle,
|
||||
// Which is a little plus sign that appears at the origin of the segment
|
||||
// and can be dragged to insert a new segment
|
||||
const extraSegmentGroup = createExtraSegmentHandle(scale, texture, theme)
|
||||
|
||||
// Segment decorators that only apply to non-close segments
|
||||
if (callExpName !== 'close') {
|
||||
// an arrowhead that appears at the end of the segment
|
||||
const arrowGroup = createArrowhead(scale, theme, color)
|
||||
// A length indicator that appears at the midpoint of the segment
|
||||
const lengthIndicatorGroup = createLengthIndicator({
|
||||
from,
|
||||
to,
|
||||
scale,
|
||||
})
|
||||
segmentGroup.add(arrowGroup)
|
||||
segmentGroup.add(lengthIndicatorGroup)
|
||||
}
|
||||
|
||||
segmentGroup.add(mesh, extraSegmentGroup)
|
||||
let updateOverlaysCallback = this.update({
|
||||
prevSegment,
|
||||
input,
|
||||
group: segmentGroup,
|
||||
scale,
|
||||
sceneInfra,
|
||||
})
|
||||
if (err(updateOverlaysCallback)) return updateOverlaysCallback
|
||||
|
||||
return {
|
||||
group: segmentGroup,
|
||||
updateOverlaysCallback,
|
||||
}
|
||||
}
|
||||
|
||||
update: SegmentUtils['update'] = ({
|
||||
input,
|
||||
group,
|
||||
scale = 1,
|
||||
sceneInfra,
|
||||
}) => {
|
||||
if (input.type !== 'straight-segment')
|
||||
return new Error('Invalid segment type')
|
||||
const { from, to } = input
|
||||
group.userData.from = from
|
||||
group.userData.to = to
|
||||
const shape = new Shape()
|
||||
shape.moveTo(0, (-SEGMENT_WIDTH_PX / 2) * scale) // The width of the line in px (2.4px in this case)
|
||||
shape.lineTo(0, (SEGMENT_WIDTH_PX / 2) * scale)
|
||||
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
|
||||
const labelGroup = group.getObjectByName(SEGMENT_LENGTH_LABEL) as Group
|
||||
|
||||
const length = Math.sqrt(
|
||||
Math.pow(to[0] - from[0], 2) + Math.pow(to[1] - from[1], 2)
|
||||
)
|
||||
|
||||
const pxLength = length / scale
|
||||
const shouldHideIdle = pxLength < HIDE_SEGMENT_LENGTH
|
||||
const shouldHideHover = pxLength < HIDE_HOVER_SEGMENT_LENGTH
|
||||
|
||||
const hoveredParent =
|
||||
sceneInfra.hoveredObject &&
|
||||
getParentGroup(sceneInfra.hoveredObject, [STRAIGHT_SEGMENT])
|
||||
let isHandlesVisible = !shouldHideIdle
|
||||
if (hoveredParent && hoveredParent?.uuid === group?.uuid) {
|
||||
isHandlesVisible = !shouldHideHover
|
||||
}
|
||||
|
||||
if (arrowGroup) {
|
||||
arrowGroup.position.set(to[0], to[1], 0)
|
||||
|
||||
const dir = new Vector3()
|
||||
.subVectors(
|
||||
new Vector3(to[0], to[1], 0),
|
||||
new Vector3(from[0], from[1], 0)
|
||||
)
|
||||
.normalize()
|
||||
arrowGroup.quaternion.setFromUnitVectors(new Vector3(0, 1, 0), dir)
|
||||
arrowGroup.scale.set(scale, scale, scale)
|
||||
arrowGroup.visible = isHandlesVisible
|
||||
}
|
||||
|
||||
const extraSegmentGroup = group.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
||||
if (extraSegmentGroup) {
|
||||
const offsetFromBase = new Vector2(to[0] - from[0], to[1] - from[1])
|
||||
.normalize()
|
||||
.multiplyScalar(EXTRA_SEGMENT_OFFSET_PX * scale)
|
||||
extraSegmentGroup.position.set(
|
||||
from[0] + offsetFromBase.x,
|
||||
from[1] + offsetFromBase.y,
|
||||
0
|
||||
)
|
||||
extraSegmentGroup.scale.set(scale, scale, scale)
|
||||
extraSegmentGroup.visible = isHandlesVisible
|
||||
}
|
||||
|
||||
if (labelGroup) {
|
||||
const labelWrapper = labelGroup.getObjectByName(
|
||||
SEGMENT_LENGTH_LABEL_TEXT
|
||||
) as CSS2DObject
|
||||
const labelWrapperElem = labelWrapper.element as HTMLDivElement
|
||||
const label = labelWrapperElem.children[0] as HTMLParagraphElement
|
||||
label.innerText = `${roundOff(length)}`
|
||||
label.classList.add(SEGMENT_LENGTH_LABEL_TEXT)
|
||||
const slope = (to[1] - from[1]) / (to[0] - from[0])
|
||||
let slopeAngle = ((Math.atan(slope) * 180) / Math.PI) * -1
|
||||
label.style.setProperty('--degree', `${slopeAngle}deg`)
|
||||
label.style.setProperty('--x', `0px`)
|
||||
label.style.setProperty('--y', `0px`)
|
||||
labelWrapper.position.set((from[0] + to[0]) / 2, (from[1] + to[1]) / 2, 0)
|
||||
labelGroup.visible = isHandlesVisible
|
||||
}
|
||||
|
||||
const straightSegmentBody = group.children.find(
|
||||
(child) => child.userData.type === STRAIGHT_SEGMENT_BODY
|
||||
) as Mesh
|
||||
if (straightSegmentBody) {
|
||||
const line = new LineCurve3(
|
||||
new Vector3(from[0], from[1], 0),
|
||||
new Vector3(to[0], to[1], 0)
|
||||
)
|
||||
straightSegmentBody.geometry = new ExtrudeGeometry(shape, {
|
||||
steps: 2,
|
||||
bevelEnabled: false,
|
||||
extrudePath: line,
|
||||
})
|
||||
}
|
||||
const straightSegmentBodyDashed = group.children.find(
|
||||
(child) => child.userData.type === STRAIGHT_SEGMENT_DASH
|
||||
) as Mesh
|
||||
if (straightSegmentBodyDashed) {
|
||||
straightSegmentBodyDashed.geometry = dashedStraight(
|
||||
from,
|
||||
to,
|
||||
shape,
|
||||
scale
|
||||
)
|
||||
}
|
||||
return () =>
|
||||
sceneInfra.updateOverlayDetails({
|
||||
arrowGroup,
|
||||
group,
|
||||
isHandlesVisible,
|
||||
from,
|
||||
to,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class TangentialArcToSegment implements SegmentUtils {
|
||||
init: SegmentUtils['init'] = ({
|
||||
prevSegment,
|
||||
input,
|
||||
id,
|
||||
pathToNode,
|
||||
isDraftSegment,
|
||||
scale = 1,
|
||||
texture,
|
||||
theme,
|
||||
isSelected,
|
||||
sceneInfra,
|
||||
}) => {
|
||||
if (input.type !== 'straight-segment')
|
||||
return new Error('Invalid segment type')
|
||||
const { from, to } = input
|
||||
const meshName = isDraftSegment
|
||||
? TANGENTIAL_ARC_TO__SEGMENT_DASH
|
||||
: TANGENTIAL_ARC_TO_SEGMENT_BODY
|
||||
|
||||
const group = new Group()
|
||||
const geometry = createArcGeometry({
|
||||
center: [0, 0],
|
||||
radius: 1,
|
||||
startAngle: 0,
|
||||
endAngle: 1,
|
||||
ccw: true,
|
||||
isDashed: isDraftSegment,
|
||||
scale,
|
||||
})
|
||||
const baseColor = getThemeColorForThreeJs(theme)
|
||||
const color = isSelected ? 0x0000ff : baseColor
|
||||
const body = new MeshBasicMaterial({ color })
|
||||
const mesh = new Mesh(geometry, body)
|
||||
const arrowGroup = createArrowhead(scale, theme, color)
|
||||
const extraSegmentGroup = createExtraSegmentHandle(scale, texture, theme)
|
||||
|
||||
group.name = TANGENTIAL_ARC_TO_SEGMENT
|
||||
mesh.userData.type = meshName
|
||||
mesh.name = meshName
|
||||
group.userData = {
|
||||
type: TANGENTIAL_ARC_TO_SEGMENT,
|
||||
id,
|
||||
from,
|
||||
to,
|
||||
prevSegment,
|
||||
pathToNode,
|
||||
isSelected,
|
||||
baseColor,
|
||||
}
|
||||
|
||||
group.add(mesh, arrowGroup, extraSegmentGroup)
|
||||
const updateOverlaysCallback = this.update({
|
||||
prevSegment,
|
||||
input,
|
||||
group,
|
||||
scale,
|
||||
sceneInfra,
|
||||
})
|
||||
if (err(updateOverlaysCallback)) return updateOverlaysCallback
|
||||
|
||||
return {
|
||||
group,
|
||||
updateOverlaysCallback,
|
||||
}
|
||||
}
|
||||
|
||||
update: SegmentUtils['update'] = ({
|
||||
prevSegment,
|
||||
input,
|
||||
group,
|
||||
scale = 1,
|
||||
sceneInfra,
|
||||
}) => {
|
||||
if (input.type !== 'straight-segment')
|
||||
return new Error('Invalid segment type')
|
||||
const { from, to } = input
|
||||
group.userData.from = from
|
||||
group.userData.to = to
|
||||
group.userData.prevSegment = prevSegment
|
||||
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
|
||||
const extraSegmentGroup = group.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
||||
|
||||
const previousPoint =
|
||||
prevSegment?.type === 'TangentialArcTo'
|
||||
? getTangentPointFromPreviousArc(
|
||||
prevSegment.center,
|
||||
prevSegment.ccw,
|
||||
prevSegment.to
|
||||
)
|
||||
: prevSegment.from
|
||||
|
||||
const arcInfo = getTangentialArcToInfo({
|
||||
arcStartPoint: from,
|
||||
arcEndPoint: to,
|
||||
tanPreviousPoint: previousPoint,
|
||||
obtuse: true,
|
||||
})
|
||||
|
||||
const pxLength = arcInfo.arcLength / scale
|
||||
const shouldHideIdle = pxLength < HIDE_SEGMENT_LENGTH
|
||||
const shouldHideHover = pxLength < HIDE_HOVER_SEGMENT_LENGTH
|
||||
|
||||
const hoveredParent =
|
||||
sceneInfra?.hoveredObject &&
|
||||
getParentGroup(sceneInfra.hoveredObject, [TANGENTIAL_ARC_TO_SEGMENT])
|
||||
let isHandlesVisible = !shouldHideIdle
|
||||
if (hoveredParent && hoveredParent?.uuid === group?.uuid) {
|
||||
isHandlesVisible = !shouldHideHover
|
||||
}
|
||||
|
||||
if (arrowGroup) {
|
||||
arrowGroup.position.set(to[0], to[1], 0)
|
||||
|
||||
const arrowheadAngle =
|
||||
arcInfo.endAngle + (Math.PI / 2) * (arcInfo.ccw ? 1 : -1)
|
||||
arrowGroup.quaternion.setFromUnitVectors(
|
||||
new Vector3(0, 1, 0),
|
||||
new Vector3(Math.cos(arrowheadAngle), Math.sin(arrowheadAngle), 0)
|
||||
)
|
||||
arrowGroup.scale.set(scale, scale, scale)
|
||||
arrowGroup.visible = isHandlesVisible
|
||||
}
|
||||
|
||||
if (extraSegmentGroup) {
|
||||
const circumferenceInPx = (2 * Math.PI * arcInfo.radius) / scale
|
||||
const extraSegmentAngleDelta =
|
||||
(EXTRA_SEGMENT_OFFSET_PX / circumferenceInPx) * Math.PI * 2
|
||||
const extraSegmentAngle =
|
||||
arcInfo.startAngle + (arcInfo.ccw ? 1 : -1) * extraSegmentAngleDelta
|
||||
const extraSegmentOffset = new Vector2(
|
||||
Math.cos(extraSegmentAngle) * arcInfo.radius,
|
||||
Math.sin(extraSegmentAngle) * arcInfo.radius
|
||||
)
|
||||
extraSegmentGroup.position.set(
|
||||
arcInfo.center[0] + extraSegmentOffset.x,
|
||||
arcInfo.center[1] + extraSegmentOffset.y,
|
||||
0
|
||||
)
|
||||
extraSegmentGroup.scale.set(scale, scale, scale)
|
||||
extraSegmentGroup.visible = isHandlesVisible
|
||||
}
|
||||
|
||||
const tangentialArcToSegmentBody = group.children.find(
|
||||
(child) => child.userData.type === TANGENTIAL_ARC_TO_SEGMENT_BODY
|
||||
) as Mesh
|
||||
|
||||
if (tangentialArcToSegmentBody) {
|
||||
const newGeo = createArcGeometry({ ...arcInfo, scale })
|
||||
tangentialArcToSegmentBody.geometry = newGeo
|
||||
}
|
||||
const tangentialArcToSegmentBodyDashed = group.getObjectByName(
|
||||
TANGENTIAL_ARC_TO__SEGMENT_DASH
|
||||
)
|
||||
if (tangentialArcToSegmentBodyDashed instanceof Mesh) {
|
||||
tangentialArcToSegmentBodyDashed.geometry = createArcGeometry({
|
||||
...arcInfo,
|
||||
isDashed: true,
|
||||
scale,
|
||||
})
|
||||
}
|
||||
const angle = normaliseAngle(
|
||||
(arcInfo.endAngle * 180) / Math.PI + (arcInfo.ccw ? 90 : -90)
|
||||
)
|
||||
return () =>
|
||||
sceneInfra.updateOverlayDetails({
|
||||
arrowGroup,
|
||||
group,
|
||||
isHandlesVisible,
|
||||
from,
|
||||
to,
|
||||
angle,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function createProfileStartHandle({
|
||||
from,
|
||||
id,
|
||||
pathToNode,
|
||||
@ -85,127 +516,6 @@ export function profileStart({
|
||||
return group
|
||||
}
|
||||
|
||||
export function straightSegment({
|
||||
from,
|
||||
to,
|
||||
id,
|
||||
pathToNode,
|
||||
isDraftSegment,
|
||||
scale = 1,
|
||||
callExpName,
|
||||
texture,
|
||||
theme,
|
||||
isSelected = false,
|
||||
}: {
|
||||
from: Coords2d
|
||||
to: Coords2d
|
||||
id: string
|
||||
pathToNode: PathToNode
|
||||
isDraftSegment?: boolean
|
||||
scale?: number
|
||||
callExpName: string
|
||||
texture: Texture
|
||||
theme: Themes
|
||||
isSelected?: boolean
|
||||
}): Group {
|
||||
const segmentGroup = new Group()
|
||||
|
||||
const shape = new Shape()
|
||||
shape.moveTo(0, (-SEGMENT_WIDTH_PX / 2) * scale)
|
||||
shape.lineTo(0, (SEGMENT_WIDTH_PX / 2) * scale)
|
||||
|
||||
let geometry
|
||||
if (isDraftSegment) {
|
||||
geometry = dashedStraight(from, to, shape, scale)
|
||||
} else {
|
||||
const line = new LineCurve3(
|
||||
new Vector3(from[0], from[1], 0),
|
||||
new Vector3(to[0], to[1], 0)
|
||||
)
|
||||
|
||||
geometry = new ExtrudeGeometry(shape, {
|
||||
steps: 2,
|
||||
bevelEnabled: false,
|
||||
extrudePath: line,
|
||||
})
|
||||
}
|
||||
|
||||
const baseColor =
|
||||
callExpName === 'close' ? 0x444444 : getThemeColorForThreeJs(theme)
|
||||
const color = isSelected ? 0x0000ff : baseColor
|
||||
const body = new MeshBasicMaterial({ color })
|
||||
const mesh = new Mesh(geometry, body)
|
||||
mesh.userData.type = isDraftSegment
|
||||
? STRAIGHT_SEGMENT_DASH
|
||||
: STRAIGHT_SEGMENT_BODY
|
||||
mesh.name = STRAIGHT_SEGMENT_BODY
|
||||
|
||||
segmentGroup.userData = {
|
||||
type: STRAIGHT_SEGMENT,
|
||||
id,
|
||||
from,
|
||||
to,
|
||||
pathToNode,
|
||||
isSelected,
|
||||
callExpName,
|
||||
baseColor,
|
||||
}
|
||||
segmentGroup.name = STRAIGHT_SEGMENT
|
||||
segmentGroup.add(mesh)
|
||||
|
||||
const length = Math.sqrt(
|
||||
Math.pow(to[0] - from[0], 2) + Math.pow(to[1] - from[1], 2)
|
||||
)
|
||||
const pxLength = length / scale
|
||||
const shouldHide = pxLength < HIDE_SEGMENT_LENGTH
|
||||
|
||||
// All segment types get an extra segment handle,
|
||||
// Which is a little plus sign that appears at the origin of the segment
|
||||
// and can be dragged to insert a new segment
|
||||
const extraSegmentGroup = createExtraSegmentHandle(scale, texture, theme)
|
||||
const directionVector = new Vector2(
|
||||
to[0] - from[0],
|
||||
to[1] - from[1]
|
||||
).normalize()
|
||||
const offsetFromBase = directionVector.multiplyScalar(
|
||||
EXTRA_SEGMENT_OFFSET_PX * scale
|
||||
)
|
||||
extraSegmentGroup.position.set(
|
||||
from[0] + offsetFromBase.x,
|
||||
from[1] + offsetFromBase.y,
|
||||
0
|
||||
)
|
||||
extraSegmentGroup.visible = !shouldHide
|
||||
segmentGroup.add(extraSegmentGroup)
|
||||
|
||||
// Segment decorators that only apply to non-close segments
|
||||
if (callExpName !== 'close') {
|
||||
// an arrowhead that appears at the end of the segment
|
||||
const arrowGroup = createArrowhead(scale, theme, color)
|
||||
arrowGroup.position.set(to[0], to[1], 0)
|
||||
const dir = new Vector3()
|
||||
.subVectors(
|
||||
new Vector3(to[0], to[1], 0),
|
||||
new Vector3(from[0], from[1], 0)
|
||||
)
|
||||
.normalize()
|
||||
arrowGroup.quaternion.setFromUnitVectors(new Vector3(0, 1, 0), dir)
|
||||
arrowGroup.visible = !shouldHide
|
||||
segmentGroup.add(arrowGroup)
|
||||
|
||||
// A length indicator that appears at the midpoint of the segment
|
||||
const lengthIndicatorGroup = createLengthIndicator({
|
||||
from,
|
||||
to,
|
||||
scale,
|
||||
length,
|
||||
})
|
||||
segmentGroup.add(lengthIndicatorGroup)
|
||||
}
|
||||
|
||||
return segmentGroup
|
||||
}
|
||||
|
||||
function createArrowhead(scale = 1, theme: Themes, color?: number): Group {
|
||||
const baseColor = getThemeColorForThreeJs(theme)
|
||||
const arrowMaterial = new MeshBasicMaterial({
|
||||
@ -267,12 +577,12 @@ function createLengthIndicator({
|
||||
from,
|
||||
to,
|
||||
scale,
|
||||
length,
|
||||
length = 0.1,
|
||||
}: {
|
||||
from: Coords2d
|
||||
to: Coords2d
|
||||
scale: number
|
||||
length: number
|
||||
length?: number
|
||||
}) {
|
||||
const lengthIndicatorGroup = new Group()
|
||||
lengthIndicatorGroup.name = SEGMENT_LENGTH_LABEL
|
||||
@ -300,111 +610,6 @@ function createLengthIndicator({
|
||||
return lengthIndicatorGroup
|
||||
}
|
||||
|
||||
export function tangentialArcToSegment({
|
||||
prevSegment,
|
||||
from,
|
||||
to,
|
||||
id,
|
||||
pathToNode,
|
||||
isDraftSegment,
|
||||
scale = 1,
|
||||
texture,
|
||||
theme,
|
||||
isSelected,
|
||||
}: {
|
||||
prevSegment: SketchGroup['value'][number]
|
||||
from: Coords2d
|
||||
to: Coords2d
|
||||
id: string
|
||||
pathToNode: PathToNode
|
||||
isDraftSegment?: boolean
|
||||
scale?: number
|
||||
texture: Texture
|
||||
theme: Themes
|
||||
isSelected?: boolean
|
||||
}): Group {
|
||||
const group = new Group()
|
||||
|
||||
const previousPoint =
|
||||
prevSegment?.type === 'TangentialArcTo'
|
||||
? getTangentPointFromPreviousArc(
|
||||
prevSegment.center,
|
||||
prevSegment.ccw,
|
||||
prevSegment.to
|
||||
)
|
||||
: prevSegment.from
|
||||
|
||||
const { center, radius, startAngle, endAngle, ccw, arcLength } =
|
||||
getTangentialArcToInfo({
|
||||
arcStartPoint: from,
|
||||
arcEndPoint: to,
|
||||
tanPreviousPoint: previousPoint,
|
||||
obtuse: true,
|
||||
})
|
||||
|
||||
const geometry = createArcGeometry({
|
||||
center,
|
||||
radius,
|
||||
startAngle,
|
||||
endAngle,
|
||||
ccw,
|
||||
isDashed: isDraftSegment,
|
||||
scale,
|
||||
})
|
||||
|
||||
const baseColor = getThemeColorForThreeJs(theme)
|
||||
const color = isSelected ? 0x0000ff : baseColor
|
||||
const body = new MeshBasicMaterial({ color })
|
||||
const mesh = new Mesh(geometry, body)
|
||||
mesh.userData.type = isDraftSegment
|
||||
? TANGENTIAL_ARC_TO__SEGMENT_DASH
|
||||
: TANGENTIAL_ARC_TO_SEGMENT_BODY
|
||||
|
||||
group.userData = {
|
||||
type: TANGENTIAL_ARC_TO_SEGMENT,
|
||||
id,
|
||||
from,
|
||||
to,
|
||||
prevSegment,
|
||||
pathToNode,
|
||||
isSelected,
|
||||
baseColor,
|
||||
}
|
||||
group.name = TANGENTIAL_ARC_TO_SEGMENT
|
||||
|
||||
const arrowGroup = createArrowhead(scale, theme, color)
|
||||
arrowGroup.position.set(to[0], to[1], 0)
|
||||
const arrowheadAngle = endAngle + (Math.PI / 2) * (ccw ? 1 : -1)
|
||||
arrowGroup.quaternion.setFromUnitVectors(
|
||||
new Vector3(0, 1, 0),
|
||||
new Vector3(Math.cos(arrowheadAngle), Math.sin(arrowheadAngle), 0)
|
||||
)
|
||||
const pxLength = arcLength / scale
|
||||
const shouldHide = pxLength < HIDE_SEGMENT_LENGTH
|
||||
arrowGroup.visible = !shouldHide
|
||||
|
||||
const extraSegmentGroup = createExtraSegmentHandle(scale, texture, theme)
|
||||
const circumferenceInPx = (2 * Math.PI * radius) / scale
|
||||
const extraSegmentAngleDelta =
|
||||
(EXTRA_SEGMENT_OFFSET_PX / circumferenceInPx) * Math.PI * 2
|
||||
const extraSegmentAngle = startAngle + (ccw ? 1 : -1) * extraSegmentAngleDelta
|
||||
const extraSegmentOffset = new Vector2(
|
||||
Math.cos(extraSegmentAngle) * radius,
|
||||
Math.sin(extraSegmentAngle) * radius
|
||||
)
|
||||
extraSegmentGroup.position.set(
|
||||
center[0] + extraSegmentOffset.x,
|
||||
center[1] + extraSegmentOffset.y,
|
||||
0
|
||||
)
|
||||
|
||||
extraSegmentGroup.visible = !shouldHide
|
||||
|
||||
group.add(mesh, arrowGroup, extraSegmentGroup)
|
||||
|
||||
return group
|
||||
}
|
||||
|
||||
export function createArcGeometry({
|
||||
center,
|
||||
radius,
|
||||
@ -579,3 +784,8 @@ export function dashedStraight(
|
||||
geo.userData.type = 'dashed'
|
||||
return geo
|
||||
}
|
||||
|
||||
export const segmentUtils = {
|
||||
straight: new StraightSegment(),
|
||||
tangentialArcTo: new TangentialArcToSegment(),
|
||||
} as const
|
||||
|
@ -204,6 +204,7 @@ export const ModelingMachineProvider = ({
|
||||
pathToNodeString,
|
||||
},
|
||||
})
|
||||
// overlay timeout
|
||||
}, 800) as unknown as number
|
||||
return {
|
||||
...context.segmentHoverMap,
|
||||
|
@ -10,10 +10,10 @@ import {
|
||||
transformSecondarySketchLinesTagFirst,
|
||||
getTransformInfos,
|
||||
PathToNodeMap,
|
||||
TransformInfo,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
import { TransformInfo } from 'lang/std/stdTypes'
|
||||
|
||||
export function equalAngleInfo({
|
||||
selectionRanges,
|
||||
|
@ -10,8 +10,8 @@ import {
|
||||
transformSecondarySketchLinesTagFirst,
|
||||
getTransformInfos,
|
||||
PathToNodeMap,
|
||||
TransformInfo,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { TransformInfo } from 'lang/std/stdTypes'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
|
||||
|
@ -9,8 +9,8 @@ import {
|
||||
PathToNodeMap,
|
||||
getTransformInfos,
|
||||
transformAstSketchLines,
|
||||
TransformInfo,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { TransformInfo } from 'lang/std/stdTypes'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Expr, VariableDeclarator } from '../../lang/wasm'
|
||||
import { Program, Expr, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
@ -11,8 +11,9 @@ import {
|
||||
transformSecondarySketchLinesTagFirst,
|
||||
getTransformInfos,
|
||||
PathToNodeMap,
|
||||
TransformInfo,
|
||||
isExprBinaryPart,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { TransformInfo } from 'lang/std/stdTypes'
|
||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||
import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
@ -178,11 +179,9 @@ export async function applyConstraintIntersect({
|
||||
}
|
||||
}
|
||||
// transform again but forcing certain values
|
||||
const finalValue = removeDoubleNegatives(
|
||||
valueNode as BinaryPart,
|
||||
sign,
|
||||
variableName
|
||||
)
|
||||
if (!isExprBinaryPart(valueNode))
|
||||
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||
const finalValue = removeDoubleNegatives(valueNode, sign, variableName)
|
||||
const transform2 = transformSecondarySketchLinesTagFirst({
|
||||
ast: kclManager.ast,
|
||||
selectionRanges: forcedSelectionRanges,
|
||||
|
@ -9,8 +9,8 @@ import {
|
||||
PathToNodeMap,
|
||||
getRemoveConstraintsTransforms,
|
||||
transformAstSketchLines,
|
||||
TransformInfo,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { TransformInfo } from 'lang/std/stdTypes'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Expr } from '../../lang/wasm'
|
||||
import { Program, Expr } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
@ -9,8 +9,9 @@ import {
|
||||
getTransformInfos,
|
||||
transformAstSketchLines,
|
||||
PathToNodeMap,
|
||||
TransformInfo,
|
||||
isExprBinaryPart,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { TransformInfo } from 'lang/std/stdTypes'
|
||||
import {
|
||||
SetAngleLengthModal,
|
||||
createSetAngleLengthModal,
|
||||
@ -121,11 +122,9 @@ export async function applyConstraintAbsDistance({
|
||||
value: forceVal,
|
||||
valueName: constraint === 'yAbs' ? 'yDis' : 'xDis',
|
||||
})
|
||||
let finalValue = removeDoubleNegatives(
|
||||
valueNode as BinaryPart,
|
||||
sign,
|
||||
variableName
|
||||
)
|
||||
if (!isExprBinaryPart(valueNode))
|
||||
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||
let finalValue = removeDoubleNegatives(valueNode, sign, variableName)
|
||||
|
||||
const transform2 = transformAstSketchLines({
|
||||
ast: structuredClone(kclManager.ast),
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Expr, VariableDeclarator } from '../../lang/wasm'
|
||||
import { Program, Expr, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
@ -10,8 +10,9 @@ import {
|
||||
transformSecondarySketchLinesTagFirst,
|
||||
getTransformInfos,
|
||||
PathToNodeMap,
|
||||
TransformInfo,
|
||||
isExprBinaryPart,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { TransformInfo } from 'lang/std/stdTypes'
|
||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||
import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
@ -133,11 +134,9 @@ export async function applyConstraintAngleBetween({
|
||||
}
|
||||
}
|
||||
|
||||
const finalValue = removeDoubleNegatives(
|
||||
valueNode as BinaryPart,
|
||||
sign,
|
||||
variableName
|
||||
)
|
||||
if (!isExprBinaryPart(valueNode))
|
||||
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||
const finalValue = removeDoubleNegatives(valueNode, sign, variableName)
|
||||
// transform again but forcing certain values
|
||||
const transformed2 = transformSecondarySketchLinesTagFirst({
|
||||
ast: kclManager.ast,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { BinaryPart, Program, Expr, VariableDeclarator } from '../../lang/wasm'
|
||||
import { Program, Expr, VariableDeclarator } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
@ -9,8 +9,9 @@ import {
|
||||
transformSecondarySketchLinesTagFirst,
|
||||
getTransformInfos,
|
||||
PathToNodeMap,
|
||||
TransformInfo,
|
||||
isExprBinaryPart,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { TransformInfo } from 'lang/std/stdTypes'
|
||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
|
||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
@ -139,9 +140,11 @@ export async function applyConstraintHorzVertDistance({
|
||||
pathToNodeMap,
|
||||
}
|
||||
} else {
|
||||
if (!isExprBinaryPart(valueNode))
|
||||
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||
let finalValue = isAlign
|
||||
? createLiteral(0)
|
||||
: removeDoubleNegatives(valueNode as BinaryPart, sign, variableName)
|
||||
: removeDoubleNegatives(valueNode, sign, variableName)
|
||||
// transform again but forcing certain values
|
||||
const transformed = transformSecondarySketchLinesTagFirst({
|
||||
ast: kclManager.ast,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { BinaryPart, Program, Expr } from '../../lang/wasm'
|
||||
import { Program, Expr } from '../../lang/wasm'
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
@ -8,9 +8,10 @@ import {
|
||||
import {
|
||||
PathToNodeMap,
|
||||
getTransformInfos,
|
||||
isExprBinaryPart,
|
||||
transformAstSketchLines,
|
||||
TransformInfo,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { TransformInfo } from 'lang/std/stdTypes'
|
||||
import {
|
||||
SetAngleLengthModal,
|
||||
createSetAngleLengthModal,
|
||||
@ -125,12 +126,9 @@ export async function applyConstraintAngleLength({
|
||||
valueName: angleOrLength === 'setAngle' ? 'angle' : 'length',
|
||||
shouldCreateVariable: true,
|
||||
})
|
||||
|
||||
let finalValue = removeDoubleNegatives(
|
||||
valueNode as BinaryPart,
|
||||
sign,
|
||||
variableName
|
||||
)
|
||||
if (!isExprBinaryPart(valueNode))
|
||||
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||
let finalValue = removeDoubleNegatives(valueNode, sign, variableName)
|
||||
if (
|
||||
isReferencingYAxisAngle ||
|
||||
(isReferencingXAxisAngle && calcIdentifier.name !== 'ZERO')
|
||||
|
@ -26,9 +26,6 @@ export type ToolTip =
|
||||
| 'tangentialArcTo'
|
||||
|
||||
export const toolTips = [
|
||||
'sketch_line',
|
||||
'move',
|
||||
// original tooltips
|
||||
'line',
|
||||
'lineTo',
|
||||
'angledLine',
|
||||
@ -42,7 +39,7 @@ export const toolTips = [
|
||||
'yLineTo',
|
||||
'angledLineThatIntersects',
|
||||
'tangentialArcTo',
|
||||
] as any as ToolTip[]
|
||||
]
|
||||
|
||||
export async function executeAst({
|
||||
ast,
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
import { enginelessExecutor } from '../lib/testHelpers'
|
||||
import { findUsesOfTagInPipe, getNodePathFromSourceRange } from './queryAst'
|
||||
import { err } from 'lib/trap'
|
||||
import { SimplifiedArgDetails } from './std/stdTypes'
|
||||
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
@ -627,7 +628,7 @@ describe('Testing removeSingleConstraintInfo', () => {
|
||||
'offset',
|
||||
],
|
||||
['tangentialArcTo([3.14 + 0, 13.14], %)', 'arrayIndex', 1],
|
||||
])('stdlib fn: %s', async (expectedFinish, key, value) => {
|
||||
] as const)('stdlib fn: %s', async (expectedFinish, key, value) => {
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
|
||||
@ -638,11 +639,27 @@ describe('Testing removeSingleConstraintInfo', () => {
|
||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||
]
|
||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||
let argPosition: SimplifiedArgDetails
|
||||
if (key === 'arrayIndex' && typeof value === 'number') {
|
||||
argPosition = {
|
||||
type: 'arrayItem',
|
||||
index: value === 0 ? 0 : 1,
|
||||
}
|
||||
} else if (key === 'objectProperty' && typeof value === 'string') {
|
||||
argPosition = {
|
||||
type: 'objectProperty',
|
||||
key: value,
|
||||
}
|
||||
} else if (key === '') {
|
||||
argPosition = {
|
||||
type: 'singleValue',
|
||||
}
|
||||
} else {
|
||||
throw new Error('argPosition is undefined')
|
||||
}
|
||||
const mod = removeSingleConstraintInfo(
|
||||
{
|
||||
pathToCallExp: pathToNode,
|
||||
[key]: value,
|
||||
},
|
||||
pathToNode,
|
||||
argPosition,
|
||||
ast,
|
||||
programMemory
|
||||
)
|
||||
@ -675,12 +692,24 @@ describe('Testing removeSingleConstraintInfo', () => {
|
||||
code.indexOf(lineOfInterest) + 1,
|
||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||
]
|
||||
let argPosition: SimplifiedArgDetails
|
||||
if (key === 'arrayIndex' && typeof value === 'number') {
|
||||
argPosition = {
|
||||
type: 'arrayItem',
|
||||
index: value === 0 ? 0 : 1,
|
||||
}
|
||||
} else if (key === 'objectProperty' && typeof value === 'string') {
|
||||
argPosition = {
|
||||
type: 'objectProperty',
|
||||
key: value,
|
||||
}
|
||||
} else {
|
||||
throw new Error('argPosition is undefined')
|
||||
}
|
||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||
const mod = removeSingleConstraintInfo(
|
||||
{
|
||||
pathToCallExp: pathToNode,
|
||||
[key]: value,
|
||||
},
|
||||
pathToNode,
|
||||
argPosition,
|
||||
ast,
|
||||
programMemory
|
||||
)
|
||||
|
@ -38,7 +38,7 @@ import {
|
||||
import { DefaultPlaneStr } from 'clientSideScene/sceneEntities'
|
||||
import { isOverlap, roundOff } from 'lib/utils'
|
||||
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
|
||||
import { ConstrainInfo } from './std/stdTypes'
|
||||
import { SimplifiedArgDetails } from './std/stdTypes'
|
||||
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
||||
import { Models } from '@kittycad/lib'
|
||||
|
||||
@ -799,15 +799,10 @@ export function deleteSegmentFromPipeExpression(
|
||||
)
|
||||
if (!constraintInfo) return
|
||||
|
||||
const input = makeRemoveSingleConstraintInput(
|
||||
constraintInfo.argPosition,
|
||||
callExp.shallowPath
|
||||
)
|
||||
if (!input) return
|
||||
if (!constraintInfo.argPosition) return
|
||||
const transform = removeSingleConstraintInfo(
|
||||
{
|
||||
...input,
|
||||
},
|
||||
callExp.shallowPath,
|
||||
constraintInfo.argPosition,
|
||||
_modifiedAst,
|
||||
programMemory
|
||||
)
|
||||
@ -834,37 +829,9 @@ export function deleteSegmentFromPipeExpression(
|
||||
return _modifiedAst
|
||||
}
|
||||
|
||||
export function makeRemoveSingleConstraintInput(
|
||||
argPosition: ConstrainInfo['argPosition'],
|
||||
pathToNode: PathToNode
|
||||
): Parameters<typeof removeSingleConstraintInfo>[0] | false {
|
||||
return argPosition?.type === 'singleValue'
|
||||
? {
|
||||
pathToCallExp: pathToNode,
|
||||
}
|
||||
: argPosition?.type === 'arrayItem'
|
||||
? {
|
||||
pathToCallExp: pathToNode,
|
||||
arrayIndex: argPosition.index,
|
||||
}
|
||||
: argPosition?.type === 'objectProperty'
|
||||
? {
|
||||
pathToCallExp: pathToNode,
|
||||
objectProperty: argPosition.key,
|
||||
}
|
||||
: false
|
||||
}
|
||||
|
||||
export function removeSingleConstraintInfo(
|
||||
{
|
||||
pathToCallExp,
|
||||
arrayIndex,
|
||||
objectProperty,
|
||||
}: {
|
||||
pathToCallExp: PathToNode
|
||||
arrayIndex?: number
|
||||
objectProperty?: string
|
||||
},
|
||||
pathToCallExp: PathToNode,
|
||||
argDetails: SimplifiedArgDetails,
|
||||
ast: Program,
|
||||
programMemory: ProgramMemory
|
||||
):
|
||||
@ -875,8 +842,7 @@ export function removeSingleConstraintInfo(
|
||||
| false {
|
||||
const transform = removeSingleConstraint({
|
||||
pathToCallExp,
|
||||
arrayIndex,
|
||||
objectProperty,
|
||||
inputDetails: argDetails,
|
||||
ast,
|
||||
})
|
||||
if (!transform) return false
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
VariableDeclaration,
|
||||
VariableDeclarator,
|
||||
sketchGroupFromKclValue,
|
||||
ObjectExpression,
|
||||
} from './wasm'
|
||||
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
|
||||
import { getSketchSegmentFromSourceRange } from './std/sketchConstraints'
|
||||
@ -934,3 +935,12 @@ export function hasExtrudableGeometry(ast: Program) {
|
||||
})
|
||||
return Object.keys(theMap).length > 0
|
||||
}
|
||||
|
||||
export function getObjExprProperty(
|
||||
node: ObjectExpression,
|
||||
propName: string
|
||||
): { expr: Expr; index: number } | null {
|
||||
const index = node.properties.findIndex(({ key }) => key.name === propName)
|
||||
if (index === -1) return null
|
||||
return { expr: node.properties[index].value, index }
|
||||
}
|
||||
|
@ -123,8 +123,11 @@ describe('testing changeSketchArguments', () => {
|
||||
ast,
|
||||
programMemory,
|
||||
[sourceStart, sourceStart + lineToChange.length],
|
||||
[2, 3],
|
||||
[0, 0]
|
||||
{
|
||||
type: 'straight-segment',
|
||||
from: [0, 0],
|
||||
to: [2, 3],
|
||||
}
|
||||
)
|
||||
if (err(changeSketchArgsRetVal)) return changeSketchArgsRetVal
|
||||
expect(recast(changeSketchArgsRetVal.modifiedAst)).toBe(expectedCode)
|
||||
@ -150,8 +153,11 @@ const mySketch001 = startSketchOn('XY')
|
||||
const newSketchLnRetVal = addNewSketchLn({
|
||||
node: ast,
|
||||
programMemory,
|
||||
to: [2, 3],
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
from: [0, 0],
|
||||
to: [2, 3],
|
||||
},
|
||||
fnName: 'lineTo',
|
||||
pathToNode: [
|
||||
['body', ''],
|
||||
|
@ -9,7 +9,6 @@ import {
|
||||
CallExpression,
|
||||
VariableDeclarator,
|
||||
Expr,
|
||||
Literal,
|
||||
VariableDeclaration,
|
||||
Identifier,
|
||||
sketchGroupFromKclValue,
|
||||
@ -20,7 +19,6 @@ import {
|
||||
getNodePathFromSourceRange,
|
||||
} from 'lang/queryAst'
|
||||
import {
|
||||
LineInputsType,
|
||||
isLiteralArrayOrStatic,
|
||||
isNotLiteralArrayOrStatic,
|
||||
} from 'lang/std/sketchcombos'
|
||||
@ -29,15 +27,15 @@ import { createPipeExpression, splitPathAtPipeExpression } from '../modifyAst'
|
||||
|
||||
import {
|
||||
SketchLineHelper,
|
||||
TransformCallback,
|
||||
ConstrainInfo,
|
||||
RawValues,
|
||||
ArrayItemInput,
|
||||
ObjectPropertyInput,
|
||||
SingleValueInput,
|
||||
VarValueKeys,
|
||||
ArrayOrObjItemInput,
|
||||
AddTagInfo,
|
||||
SegmentInputs,
|
||||
SimplifiedArgDetails,
|
||||
RawArgs,
|
||||
CreatedSketchExprResult,
|
||||
} from 'lang/std/stdTypes'
|
||||
|
||||
import {
|
||||
@ -56,6 +54,10 @@ import { err } from 'lib/trap'
|
||||
import { perpendicularDistance } from 'sketch-helpers'
|
||||
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
||||
|
||||
const STRAIGHT_SEGMENT_ERR = new Error(
|
||||
'Invalid input, expected "straight-segment"'
|
||||
)
|
||||
|
||||
export type Coords2d = [number, number]
|
||||
|
||||
export function getCoordsFromPaths(skGroup: SketchGroup, index = 0): Coords2d {
|
||||
@ -109,6 +111,7 @@ type AbbreviatedInput =
|
||||
| ArrayItemInput<any>['index']
|
||||
| ObjectPropertyInput<any>['key']
|
||||
| SingleValueInput<any>['type']
|
||||
| SimplifiedArgDetails
|
||||
| undefined
|
||||
|
||||
const constrainInfo = (
|
||||
@ -157,8 +160,12 @@ const commonConstraintInfoHelper = (
|
||||
const firstArg = callExp.arguments?.[0]
|
||||
const isArr = firstArg.type === 'ArrayExpression'
|
||||
if (!isArr && firstArg.type !== 'ObjectExpression') return []
|
||||
const pipeExpressionIndex = pathToNode.findIndex(
|
||||
([_, nodeName]) => nodeName === 'PipeExpression'
|
||||
)
|
||||
const pathToBase = pathToNode.slice(0, pipeExpressionIndex + 2)
|
||||
const pathToArrayExpression: PathToNode = [
|
||||
...pathToNode,
|
||||
...pathToBase,
|
||||
['arguments', 'CallExpression'],
|
||||
[0, 'index'],
|
||||
isArr
|
||||
@ -270,45 +277,6 @@ const horzVertConstraintInfoHelper = (
|
||||
]
|
||||
}
|
||||
|
||||
function arrayRawValuesHelper(a: Array<[Literal, LineInputsType]>): RawValues {
|
||||
return a.map(
|
||||
([literal, argType], index): ArrayItemInput<Literal> => ({
|
||||
type: 'arrayItem',
|
||||
index: index === 0 ? 0 : 1,
|
||||
argType,
|
||||
value: literal,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function arrOrObjectRawValuesHelper(
|
||||
a: Array<[Literal, LineInputsType, VarValueKeys]>
|
||||
): RawValues {
|
||||
return a.map(
|
||||
([literal, argType, key], index): ArrayOrObjItemInput<Literal> => ({
|
||||
type: 'arrayOrObjItem',
|
||||
// key: argType,w
|
||||
index: index === 0 ? 0 : 1,
|
||||
key,
|
||||
argType,
|
||||
value: literal,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function singleRawValueHelper(
|
||||
literal: Literal,
|
||||
argType: LineInputsType
|
||||
): RawValues {
|
||||
return [
|
||||
{
|
||||
type: 'singleValue',
|
||||
argType,
|
||||
value: literal,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
function getTag(index = 2): SketchLineHelper['getTag'] {
|
||||
return (callExp: CallExpression) => {
|
||||
if (callExp.type !== 'CallExpression')
|
||||
@ -322,14 +290,9 @@ function getTag(index = 2): SketchLineHelper['getTag'] {
|
||||
}
|
||||
|
||||
export const lineTo: SketchLineHelper = {
|
||||
add: ({
|
||||
node,
|
||||
pathToNode,
|
||||
to,
|
||||
createCallback,
|
||||
replaceExisting,
|
||||
referencedSegment,
|
||||
}) => {
|
||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const to = segmentInput.to
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||
_node,
|
||||
@ -349,15 +312,23 @@ export const lineTo: SketchLineHelper = {
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
if (replaceExisting && createCallback) {
|
||||
const { callExp, valueUsedInTransform } = createCallback(
|
||||
newVals,
|
||||
arrayRawValuesHelper([
|
||||
[createLiteral(roundOff(to[0], 2)), 'xAbsolute'],
|
||||
[createLiteral(roundOff(to[1], 2)), 'yAbsolute'],
|
||||
]),
|
||||
referencedSegment
|
||||
)
|
||||
if (replaceExistingCallback) {
|
||||
const result = replaceExistingCallback([
|
||||
{
|
||||
type: 'arrayItem',
|
||||
index: 0,
|
||||
argType: 'xAbsolute',
|
||||
expr: createLiteral(roundOff(to[0], 2)),
|
||||
},
|
||||
{
|
||||
type: 'arrayItem',
|
||||
index: 1,
|
||||
argType: 'yAbsolute',
|
||||
expr: createLiteral(roundOff(to[1], 2)),
|
||||
},
|
||||
])
|
||||
if (err(result)) return result
|
||||
const { callExp, valueUsedInTransform } = result
|
||||
pipe.body[callIndex] = callExp
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -372,7 +343,9 @@ export const lineTo: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to }) => {
|
||||
updateArgs: ({ node, pathToNode, input }) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to } = input
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
@ -407,13 +380,12 @@ export const line: SketchLineHelper = {
|
||||
node,
|
||||
previousProgramMemory,
|
||||
pathToNode,
|
||||
to,
|
||||
from,
|
||||
replaceExisting,
|
||||
referencedSegment,
|
||||
createCallback,
|
||||
segmentInput,
|
||||
replaceExistingCallback,
|
||||
spliceBetween,
|
||||
}) => {
|
||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { from, to } = segmentInput
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<PipeExpression | CallExpression>(
|
||||
_node,
|
||||
@ -433,7 +405,11 @@ export const line: SketchLineHelper = {
|
||||
const newXVal = createLiteral(roundOff(to[0] - from[0], 2))
|
||||
const newYVal = createLiteral(roundOff(to[1] - from[1], 2))
|
||||
|
||||
if (spliceBetween && !createCallback && pipe.type === 'PipeExpression') {
|
||||
if (
|
||||
spliceBetween &&
|
||||
!replaceExistingCallback &&
|
||||
pipe.type === 'PipeExpression'
|
||||
) {
|
||||
const callExp = createCallExpression('line', [
|
||||
createArrayExpression([newXVal, newYVal]),
|
||||
createPipeSubstitution(),
|
||||
@ -456,16 +432,24 @@ export const line: SketchLineHelper = {
|
||||
}
|
||||
}
|
||||
|
||||
if (replaceExisting && createCallback && pipe.type !== 'CallExpression') {
|
||||
if (replaceExistingCallback && pipe.type !== 'CallExpression') {
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
const { callExp, valueUsedInTransform } = createCallback(
|
||||
[newXVal, newYVal],
|
||||
arrayRawValuesHelper([
|
||||
[createLiteral(roundOff(to[0] - from[0], 2)), 'xRelative'],
|
||||
[createLiteral(roundOff(to[1] - from[1], 2)), 'yRelative'],
|
||||
]),
|
||||
referencedSegment
|
||||
)
|
||||
const result = replaceExistingCallback([
|
||||
{
|
||||
type: 'arrayItem',
|
||||
index: 0,
|
||||
argType: 'xRelative',
|
||||
expr: createLiteral(roundOff(to[0] - from[0], 2)),
|
||||
},
|
||||
{
|
||||
type: 'arrayItem',
|
||||
index: 1,
|
||||
argType: 'yRelative',
|
||||
expr: createLiteral(roundOff(to[1] - from[1], 2)),
|
||||
},
|
||||
])
|
||||
if (err(result)) return result
|
||||
const { callExp, valueUsedInTransform } = result
|
||||
pipe.body[callIndex] = callExp
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -496,7 +480,9 @@ export const line: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
updateArgs: ({ node, pathToNode, input }) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to, from } = input
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
@ -530,7 +516,9 @@ export const line: SketchLineHelper = {
|
||||
}
|
||||
|
||||
export const xLineTo: SketchLineHelper = {
|
||||
add: ({ node, pathToNode, to, replaceExisting, createCallback }) => {
|
||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to } = segmentInput
|
||||
const _node = { ...node }
|
||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||
@ -539,12 +527,17 @@ export const xLineTo: SketchLineHelper = {
|
||||
|
||||
const newVal = createLiteral(roundOff(to[0], 2))
|
||||
|
||||
if (replaceExisting && createCallback) {
|
||||
if (replaceExistingCallback) {
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
const { callExp, valueUsedInTransform } = createCallback(
|
||||
[newVal, newVal],
|
||||
singleRawValueHelper(newVal, 'xAbsolute')
|
||||
)
|
||||
const result = replaceExistingCallback([
|
||||
{
|
||||
type: 'singleValue',
|
||||
argType: 'xAbsolute',
|
||||
expr: createLiteral(roundOff(to[0], 2)),
|
||||
},
|
||||
])
|
||||
if (err(result)) return result
|
||||
const { callExp, valueUsedInTransform } = result
|
||||
pipe.body[callIndex] = callExp
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -562,7 +555,9 @@ export const xLineTo: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to }) => {
|
||||
updateArgs: ({ node, pathToNode, input }) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to } = input
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
@ -591,7 +586,9 @@ export const xLineTo: SketchLineHelper = {
|
||||
}
|
||||
|
||||
export const yLineTo: SketchLineHelper = {
|
||||
add: ({ node, pathToNode, to, replaceExisting, createCallback }) => {
|
||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to } = segmentInput
|
||||
const _node = { ...node }
|
||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||
@ -600,12 +597,17 @@ export const yLineTo: SketchLineHelper = {
|
||||
|
||||
const newVal = createLiteral(roundOff(to[1], 2))
|
||||
|
||||
if (replaceExisting && createCallback) {
|
||||
if (replaceExistingCallback) {
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
const { callExp, valueUsedInTransform } = createCallback(
|
||||
[newVal, newVal],
|
||||
singleRawValueHelper(newVal, 'yAbsolute')
|
||||
)
|
||||
const result = replaceExistingCallback([
|
||||
{
|
||||
type: 'singleValue',
|
||||
argType: 'yAbsolute',
|
||||
expr: newVal,
|
||||
},
|
||||
])
|
||||
if (err(result)) return result
|
||||
const { callExp, valueUsedInTransform } = result
|
||||
pipe.body[callIndex] = callExp
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -623,7 +625,9 @@ export const yLineTo: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
updateArgs: ({ node, pathToNode, input }) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to } = input
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
@ -652,7 +656,9 @@ export const yLineTo: SketchLineHelper = {
|
||||
}
|
||||
|
||||
export const xLine: SketchLineHelper = {
|
||||
add: ({ node, pathToNode, to, from, replaceExisting, createCallback }) => {
|
||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { from, to } = segmentInput
|
||||
const _node = { ...node }
|
||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||
@ -660,14 +666,18 @@ export const xLine: SketchLineHelper = {
|
||||
const { node: pipe } = _node1
|
||||
|
||||
const newVal = createLiteral(roundOff(to[0] - from[0], 2))
|
||||
const firstArg = newVal
|
||||
|
||||
if (replaceExisting && createCallback) {
|
||||
if (replaceExistingCallback) {
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
const { callExp, valueUsedInTransform } = createCallback(
|
||||
[firstArg, firstArg],
|
||||
singleRawValueHelper(firstArg, 'xRelative')
|
||||
)
|
||||
const result = replaceExistingCallback([
|
||||
{
|
||||
type: 'singleValue',
|
||||
argType: 'xRelative',
|
||||
expr: newVal,
|
||||
},
|
||||
])
|
||||
if (err(result)) return result
|
||||
const { callExp, valueUsedInTransform } = result
|
||||
pipe.body[callIndex] = callExp
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -677,13 +687,15 @@ export const xLine: SketchLineHelper = {
|
||||
}
|
||||
|
||||
const newLine = createCallExpression('xLine', [
|
||||
firstArg,
|
||||
newVal,
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
pipe.body = [...pipe.body, newLine]
|
||||
return { modifiedAst: _node, pathToNode }
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
updateArgs: ({ node, pathToNode, input }) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to, from } = input
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
@ -712,19 +724,26 @@ export const xLine: SketchLineHelper = {
|
||||
}
|
||||
|
||||
export const yLine: SketchLineHelper = {
|
||||
add: ({ node, pathToNode, to, from, replaceExisting, createCallback }) => {
|
||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { from, to } = segmentInput
|
||||
const _node = { ...node }
|
||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||
if (err(_node1)) return _node1
|
||||
const { node: pipe } = _node1
|
||||
const newVal = createLiteral(roundOff(to[1] - from[1], 2))
|
||||
if (replaceExisting && createCallback) {
|
||||
if (replaceExistingCallback) {
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
const { callExp, valueUsedInTransform } = createCallback(
|
||||
[newVal, newVal],
|
||||
singleRawValueHelper(newVal, 'yRelative')
|
||||
)
|
||||
const result = replaceExistingCallback([
|
||||
{
|
||||
type: 'singleValue',
|
||||
argType: 'yRelative',
|
||||
expr: newVal,
|
||||
},
|
||||
])
|
||||
if (err(result)) return result
|
||||
const { callExp, valueUsedInTransform } = result
|
||||
pipe.body[callIndex] = callExp
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -740,7 +759,9 @@ export const yLine: SketchLineHelper = {
|
||||
pipe.body = [...pipe.body, newLine]
|
||||
return { modifiedAst: _node, pathToNode }
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
updateArgs: ({ node, pathToNode, input }) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to, from } = input
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
@ -769,14 +790,9 @@ export const yLine: SketchLineHelper = {
|
||||
}
|
||||
|
||||
export const tangentialArcTo: SketchLineHelper = {
|
||||
add: ({
|
||||
node,
|
||||
pathToNode,
|
||||
to,
|
||||
createCallback,
|
||||
replaceExisting,
|
||||
referencedSegment,
|
||||
}) => {
|
||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to } = segmentInput
|
||||
const _node = { ...node }
|
||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||
const _node1 = getNode<PipeExpression | CallExpression>('PipeExpression')
|
||||
@ -793,16 +809,24 @@ export const tangentialArcTo: SketchLineHelper = {
|
||||
const toX = createLiteral(roundOff(to[0], 2))
|
||||
const toY = createLiteral(roundOff(to[1], 2))
|
||||
|
||||
if (replaceExisting && createCallback && pipe.type !== 'CallExpression') {
|
||||
if (replaceExistingCallback && pipe.type !== 'CallExpression') {
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
const { callExp, valueUsedInTransform } = createCallback(
|
||||
[toX, toY],
|
||||
arrayRawValuesHelper([
|
||||
[createLiteral(roundOff(to[0], 2)), 'xAbsolute'],
|
||||
[createLiteral(roundOff(to[1], 2)), 'yAbsolute'],
|
||||
]),
|
||||
referencedSegment
|
||||
)
|
||||
const result = replaceExistingCallback([
|
||||
{
|
||||
type: 'arrayItem',
|
||||
index: 0,
|
||||
argType: 'xRelative',
|
||||
expr: toX,
|
||||
},
|
||||
{
|
||||
type: 'arrayItem',
|
||||
index: 1,
|
||||
argType: 'yAbsolute',
|
||||
expr: toY,
|
||||
},
|
||||
])
|
||||
if (err(result)) return result
|
||||
const { callExp, valueUsedInTransform } = result
|
||||
pipe.body[callIndex] = callExp
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -832,7 +856,9 @@ export const tangentialArcTo: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
updateArgs: ({ node, pathToNode, input }) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to } = input
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
@ -900,15 +926,9 @@ export const tangentialArcTo: SketchLineHelper = {
|
||||
},
|
||||
}
|
||||
export const angledLine: SketchLineHelper = {
|
||||
add: ({
|
||||
node,
|
||||
pathToNode,
|
||||
to,
|
||||
from,
|
||||
createCallback,
|
||||
replaceExisting,
|
||||
referencedSegment,
|
||||
}) => {
|
||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { from, to } = segmentInput
|
||||
const _node = { ...node }
|
||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||
@ -922,16 +942,26 @@ export const angledLine: SketchLineHelper = {
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
|
||||
if (replaceExisting && createCallback) {
|
||||
if (replaceExistingCallback) {
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
const { callExp, valueUsedInTransform } = createCallback(
|
||||
[newAngleVal, newLengthVal],
|
||||
arrOrObjectRawValuesHelper([
|
||||
[newAngleVal, 'angle', 'angle'],
|
||||
[newLengthVal, 'length', 'length'],
|
||||
]),
|
||||
referencedSegment
|
||||
)
|
||||
const result = replaceExistingCallback([
|
||||
{
|
||||
type: 'arrayOrObjItem',
|
||||
index: 0,
|
||||
key: 'angle',
|
||||
argType: 'angle',
|
||||
expr: newAngleVal,
|
||||
},
|
||||
{
|
||||
type: 'arrayOrObjItem',
|
||||
index: 1,
|
||||
key: 'length',
|
||||
argType: 'length',
|
||||
expr: newLengthVal,
|
||||
},
|
||||
])
|
||||
if (err(result)) return result
|
||||
const { callExp, valueUsedInTransform } = result
|
||||
pipe.body[callIndex] = callExp
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
@ -946,7 +976,9 @@ export const angledLine: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
updateArgs: ({ node, pathToNode, input }) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to, from } = input
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
@ -988,11 +1020,11 @@ export const angledLineOfXLength: SketchLineHelper = {
|
||||
node,
|
||||
previousProgramMemory,
|
||||
pathToNode,
|
||||
to,
|
||||
from,
|
||||
createCallback,
|
||||
replaceExisting,
|
||||
segmentInput,
|
||||
replaceExistingCallback,
|
||||
}) => {
|
||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { from, to } = segmentInput
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||
_node,
|
||||
@ -1019,20 +1051,34 @@ export const angledLineOfXLength: SketchLineHelper = {
|
||||
}
|
||||
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
||||
const xLength = createLiteral(roundOff(Math.abs(from[0] - to[0]), 2) || 0.1)
|
||||
const newLine = createCallback
|
||||
? createCallback(
|
||||
[angle, xLength],
|
||||
arrOrObjectRawValuesHelper([
|
||||
[angle, 'angle', 'angle'],
|
||||
[xLength, 'xRelative', 'length'],
|
||||
let newLine: Expr
|
||||
if (replaceExistingCallback) {
|
||||
const result = replaceExistingCallback([
|
||||
{
|
||||
type: 'arrayOrObjItem',
|
||||
index: 0,
|
||||
key: 'angle',
|
||||
argType: 'angle',
|
||||
expr: angle,
|
||||
},
|
||||
{
|
||||
type: 'arrayOrObjItem',
|
||||
index: 1,
|
||||
key: 'length',
|
||||
argType: 'xRelative',
|
||||
expr: xLength,
|
||||
},
|
||||
])
|
||||
).callExp
|
||||
: createCallExpression('angledLineOfXLength', [
|
||||
if (err(result)) return result
|
||||
newLine = result.callExp
|
||||
} else {
|
||||
newLine = createCallExpression('angledLineOfXLength', [
|
||||
createArrayExpression([angle, xLength]),
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
}
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
if (replaceExisting) {
|
||||
if (replaceExistingCallback) {
|
||||
pipe.body[callIndex] = newLine
|
||||
} else {
|
||||
pipe.body = [...pipe.body, newLine]
|
||||
@ -1042,7 +1088,9 @@ export const angledLineOfXLength: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
updateArgs: ({ node, pathToNode, input }) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to, from } = input
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
@ -1088,11 +1136,11 @@ export const angledLineOfYLength: SketchLineHelper = {
|
||||
node,
|
||||
previousProgramMemory,
|
||||
pathToNode,
|
||||
to,
|
||||
from,
|
||||
createCallback,
|
||||
replaceExisting,
|
||||
segmentInput,
|
||||
replaceExistingCallback,
|
||||
}) => {
|
||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { from, to } = segmentInput
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||
_node,
|
||||
@ -1117,20 +1165,34 @@ export const angledLineOfYLength: SketchLineHelper = {
|
||||
|
||||
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
||||
const yLength = createLiteral(roundOff(Math.abs(from[1] - to[1]), 2) || 0.1)
|
||||
const newLine = createCallback
|
||||
? createCallback(
|
||||
[angle, yLength],
|
||||
arrOrObjectRawValuesHelper([
|
||||
[angle, 'angle', 'angle'],
|
||||
[yLength, 'yRelative', 'length'],
|
||||
let newLine: Expr
|
||||
if (replaceExistingCallback) {
|
||||
const result = replaceExistingCallback([
|
||||
{
|
||||
type: 'arrayOrObjItem',
|
||||
index: 0,
|
||||
key: 'angle',
|
||||
argType: 'angle',
|
||||
expr: angle,
|
||||
},
|
||||
{
|
||||
type: 'arrayOrObjItem',
|
||||
index: 1,
|
||||
key: 'length',
|
||||
argType: 'yRelative',
|
||||
expr: yLength,
|
||||
},
|
||||
])
|
||||
).callExp
|
||||
: createCallExpression('angledLineOfYLength', [
|
||||
if (err(result)) return result
|
||||
newLine = result.callExp
|
||||
} else {
|
||||
newLine = createCallExpression('angledLineOfYLength', [
|
||||
createArrayExpression([angle, yLength]),
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
}
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
if (replaceExisting) {
|
||||
if (replaceExistingCallback) {
|
||||
pipe.body[callIndex] = newLine
|
||||
} else {
|
||||
pipe.body = [...pipe.body, newLine]
|
||||
@ -1140,7 +1202,9 @@ export const angledLineOfYLength: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
updateArgs: ({ node, pathToNode, input }) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to, from } = input
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
@ -1182,15 +1246,9 @@ export const angledLineOfYLength: SketchLineHelper = {
|
||||
}
|
||||
|
||||
export const angledLineToX: SketchLineHelper = {
|
||||
add: ({
|
||||
node,
|
||||
pathToNode,
|
||||
to,
|
||||
from,
|
||||
createCallback,
|
||||
replaceExisting,
|
||||
referencedSegment,
|
||||
}) => {
|
||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { from, to } = segmentInput
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||
_node,
|
||||
@ -1202,15 +1260,25 @@ export const angledLineToX: SketchLineHelper = {
|
||||
const { node: pipe } = nodeMeta
|
||||
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
||||
const xArg = createLiteral(roundOff(to[0], 2))
|
||||
if (replaceExisting && createCallback) {
|
||||
const { callExp, valueUsedInTransform } = createCallback(
|
||||
[angle, xArg],
|
||||
arrOrObjectRawValuesHelper([
|
||||
[angle, 'angle', 'angle'],
|
||||
[xArg, 'xAbsolute', 'to'],
|
||||
]),
|
||||
referencedSegment
|
||||
)
|
||||
if (replaceExistingCallback) {
|
||||
const result = replaceExistingCallback([
|
||||
{
|
||||
type: 'arrayOrObjItem',
|
||||
index: 0,
|
||||
key: 'angle',
|
||||
argType: 'angle',
|
||||
expr: angle,
|
||||
},
|
||||
{
|
||||
type: 'arrayOrObjItem',
|
||||
index: 1,
|
||||
key: 'to',
|
||||
argType: 'xAbsolute',
|
||||
expr: xArg,
|
||||
},
|
||||
])
|
||||
if (err(result)) return result
|
||||
const { callExp, valueUsedInTransform } = result
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
pipe.body[callIndex] = callExp
|
||||
return {
|
||||
@ -1230,7 +1298,9 @@ export const angledLineToX: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
updateArgs: ({ node, pathToNode, input }) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to, from } = input
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
@ -1270,15 +1340,9 @@ export const angledLineToX: SketchLineHelper = {
|
||||
}
|
||||
|
||||
export const angledLineToY: SketchLineHelper = {
|
||||
add: ({
|
||||
node,
|
||||
pathToNode,
|
||||
to,
|
||||
from,
|
||||
createCallback,
|
||||
replaceExisting,
|
||||
referencedSegment,
|
||||
}) => {
|
||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { from, to } = segmentInput
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||
_node,
|
||||
@ -1292,15 +1356,25 @@ export const angledLineToY: SketchLineHelper = {
|
||||
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
||||
const yArg = createLiteral(roundOff(to[1], 2))
|
||||
|
||||
if (replaceExisting && createCallback) {
|
||||
const { callExp, valueUsedInTransform } = createCallback(
|
||||
[angle, yArg],
|
||||
arrOrObjectRawValuesHelper([
|
||||
[angle, 'angle', 'angle'],
|
||||
[yArg, 'yAbsolute', 'to'],
|
||||
]),
|
||||
referencedSegment
|
||||
)
|
||||
if (replaceExistingCallback) {
|
||||
const result = replaceExistingCallback([
|
||||
{
|
||||
type: 'arrayOrObjItem',
|
||||
index: 0,
|
||||
key: 'angle',
|
||||
argType: 'angle',
|
||||
expr: angle,
|
||||
},
|
||||
{
|
||||
type: 'arrayOrObjItem',
|
||||
index: 1,
|
||||
key: 'to',
|
||||
argType: 'yAbsolute',
|
||||
expr: yArg,
|
||||
},
|
||||
])
|
||||
if (err(result)) return result
|
||||
const { callExp, valueUsedInTransform } = result
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
pipe.body[callIndex] = callExp
|
||||
return {
|
||||
@ -1320,7 +1394,9 @@ export const angledLineToY: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from }) => {
|
||||
updateArgs: ({ node, pathToNode, input }) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to, from } = input
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
@ -1363,12 +1439,12 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
||||
add: ({
|
||||
node,
|
||||
pathToNode,
|
||||
to,
|
||||
from,
|
||||
createCallback,
|
||||
replaceExisting,
|
||||
segmentInput,
|
||||
replaceExistingCallback,
|
||||
referencedSegment,
|
||||
}) => {
|
||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { from, to } = segmentInput
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||
_node,
|
||||
@ -1395,24 +1471,23 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
||||
)
|
||||
)
|
||||
|
||||
if (replaceExisting && createCallback) {
|
||||
const { callExp, valueUsedInTransform } = createCallback(
|
||||
[angle, offset],
|
||||
[
|
||||
if (replaceExistingCallback) {
|
||||
const result = replaceExistingCallback([
|
||||
{
|
||||
type: 'objectProperty',
|
||||
key: 'angle',
|
||||
value: angle,
|
||||
argType: 'angle',
|
||||
expr: angle,
|
||||
},
|
||||
{
|
||||
type: 'objectProperty',
|
||||
key: 'offset',
|
||||
value: offset,
|
||||
argType: 'intersectionOffset',
|
||||
expr: offset,
|
||||
},
|
||||
]
|
||||
)
|
||||
])
|
||||
if (err(result)) return result
|
||||
const { callExp, valueUsedInTransform } = result
|
||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||
pipe.body[callIndex] = callExp
|
||||
return {
|
||||
@ -1423,7 +1498,9 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
||||
}
|
||||
return new Error('not implemented')
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, to, from, previousProgramMemory }) => {
|
||||
updateArgs: ({ node, pathToNode, input, previousProgramMemory }) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to, from } = input
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) return nodeMeta
|
||||
@ -1559,8 +1636,10 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
||||
export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
|
||||
node,
|
||||
pathToNode,
|
||||
to,
|
||||
input,
|
||||
}) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to } = input
|
||||
const _node = { ...node }
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||
if (err(nodeMeta)) {
|
||||
@ -1615,8 +1694,7 @@ export function changeSketchArguments(
|
||||
node: Program,
|
||||
programMemory: ProgramMemory,
|
||||
sourceRange: SourceRange,
|
||||
args: [number, number],
|
||||
from: [number, number]
|
||||
input: SegmentInputs
|
||||
): { modifiedAst: Program; pathToNode: PathToNode } | Error {
|
||||
const _node = { ...node }
|
||||
const thePath = getNodePathFromSourceRange(_node, sourceRange)
|
||||
@ -1635,8 +1713,7 @@ export function changeSketchArguments(
|
||||
node: _node,
|
||||
previousProgramMemory: programMemory,
|
||||
pathToNode: shallowPath,
|
||||
to: args,
|
||||
from,
|
||||
input,
|
||||
})
|
||||
}
|
||||
|
||||
@ -1684,8 +1761,7 @@ export function compareVec2Epsilon2(
|
||||
interface CreateLineFnCallArgs {
|
||||
node: Program
|
||||
programMemory: ProgramMemory
|
||||
to: [number, number]
|
||||
from: [number, number]
|
||||
input: SegmentInputs
|
||||
fnName: ToolTip
|
||||
pathToNode: PathToNode
|
||||
spliceBetween?: boolean
|
||||
@ -1694,10 +1770,9 @@ interface CreateLineFnCallArgs {
|
||||
export function addNewSketchLn({
|
||||
node: _node,
|
||||
programMemory: previousProgramMemory,
|
||||
to,
|
||||
fnName,
|
||||
pathToNode,
|
||||
from,
|
||||
input: segmentInput,
|
||||
spliceBetween = false,
|
||||
}: CreateLineFnCallArgs):
|
||||
| {
|
||||
@ -1721,9 +1796,7 @@ export function addNewSketchLn({
|
||||
node,
|
||||
previousProgramMemory,
|
||||
pathToNode,
|
||||
to,
|
||||
from,
|
||||
replaceExisting: false,
|
||||
segmentInput,
|
||||
spliceBetween,
|
||||
})
|
||||
}
|
||||
@ -1784,18 +1857,16 @@ export function replaceSketchLine({
|
||||
programMemory,
|
||||
pathToNode: _pathToNode,
|
||||
fnName,
|
||||
to,
|
||||
from,
|
||||
createCallback,
|
||||
segmentInput,
|
||||
replaceExistingCallback,
|
||||
referencedSegment,
|
||||
}: {
|
||||
node: Program
|
||||
programMemory: ProgramMemory
|
||||
pathToNode: PathToNode
|
||||
fnName: ToolTip
|
||||
to: [number, number]
|
||||
from: [number, number]
|
||||
createCallback: TransformCallback
|
||||
segmentInput: SegmentInputs
|
||||
replaceExistingCallback: (rawArgs: RawArgs) => CreatedSketchExprResult | Error
|
||||
referencedSegment?: Path
|
||||
}):
|
||||
| {
|
||||
@ -1805,7 +1876,7 @@ export function replaceSketchLine({
|
||||
}
|
||||
| Error {
|
||||
if (![...toolTips, 'intersect'].includes(fnName)) {
|
||||
return new Error('not a tooltip')
|
||||
return new Error(`The following function name is not tooltip: ${fnName}`)
|
||||
}
|
||||
const _node = { ...node }
|
||||
|
||||
@ -1815,10 +1886,8 @@ export function replaceSketchLine({
|
||||
previousProgramMemory: programMemory,
|
||||
pathToNode: _pathToNode,
|
||||
referencedSegment,
|
||||
to,
|
||||
from,
|
||||
replaceExisting: true,
|
||||
createCallback,
|
||||
segmentInput,
|
||||
replaceExistingCallback,
|
||||
})
|
||||
if (err(addRetVal)) return addRetVal
|
||||
|
||||
@ -1826,13 +1895,16 @@ export function replaceSketchLine({
|
||||
return { modifiedAst, valueUsedInTransform, pathToNode }
|
||||
}
|
||||
|
||||
export function addTagForSketchOnFace(a: AddTagInfo, expressionName: string) {
|
||||
export function addTagForSketchOnFace(
|
||||
tagInfo: AddTagInfo,
|
||||
expressionName: string
|
||||
) {
|
||||
if (expressionName === 'close') {
|
||||
return addTag(1)(a)
|
||||
return addTag(1)(tagInfo)
|
||||
}
|
||||
if (expressionName in sketchLineHelperMap) {
|
||||
const { addTag } = sketchLineHelperMap[expressionName]
|
||||
return addTag(a)
|
||||
return addTag(tagInfo)
|
||||
}
|
||||
return new Error(`"${expressionName}" is not a sketch line helper`)
|
||||
}
|
||||
|
@ -43,6 +43,17 @@ export function getSketchSegmentFromSourceRange(
|
||||
index: number
|
||||
}
|
||||
| Error {
|
||||
const lineIndex = sketchGroup.value.findIndex(
|
||||
({ __geoMeta: { sourceRange } }: Path) =>
|
||||
sourceRange[0] <= rangeStart && sourceRange[1] >= rangeEnd
|
||||
)
|
||||
const line = sketchGroup.value[lineIndex]
|
||||
if (line) {
|
||||
return {
|
||||
segment: line,
|
||||
index: lineIndex,
|
||||
}
|
||||
}
|
||||
const startSourceRange = sketchGroup.start?.__geoMeta.sourceRange
|
||||
if (
|
||||
startSourceRange &&
|
||||
@ -51,17 +62,7 @@ export function getSketchSegmentFromSourceRange(
|
||||
sketchGroup.start
|
||||
)
|
||||
return { segment: { ...sketchGroup.start, type: 'Base' }, index: -1 }
|
||||
|
||||
const lineIndex = sketchGroup.value.findIndex(
|
||||
({ __geoMeta: { sourceRange } }: Path) =>
|
||||
sourceRange[0] <= rangeStart && sourceRange[1] >= rangeEnd
|
||||
)
|
||||
const line = sketchGroup.value[lineIndex]
|
||||
if (!line) return new Error('could not find matching line')
|
||||
return {
|
||||
segment: line,
|
||||
index: lineIndex,
|
||||
}
|
||||
return new Error('could not find matching segment')
|
||||
}
|
||||
|
||||
export function isSketchVariablesLinked(
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,23 +8,10 @@ import {
|
||||
PathToNode,
|
||||
CallExpression,
|
||||
Literal,
|
||||
BinaryPart,
|
||||
} from '../wasm'
|
||||
import { EngineCommandManager } from './engineConnection'
|
||||
import { LineInputsType } from './sketchcombos'
|
||||
|
||||
export interface InternalFirstArg {
|
||||
programMemory: ProgramMemory
|
||||
name?: string
|
||||
sourceRange: SourceRange
|
||||
engineCommandManager: EngineCommandManager
|
||||
code: string
|
||||
}
|
||||
|
||||
export interface PathReturn {
|
||||
programMemory: ProgramMemory
|
||||
currentPath: Path
|
||||
}
|
||||
|
||||
export interface ModifyAstBase {
|
||||
node: Program
|
||||
// TODO #896: Remove ProgramMemory from this interface
|
||||
@ -37,76 +24,162 @@ export interface AddTagInfo {
|
||||
pathToNode: PathToNode
|
||||
}
|
||||
|
||||
interface addCall extends ModifyAstBase {
|
||||
to: [number, number]
|
||||
/** Inputs for all straight segments, to and from are absolute values, as this gives a
|
||||
* consistent base that can be converted to all of the line, angledLine, etc segment types
|
||||
* One notable exception to "straight segment" is that tangentialArcTo is included in this
|
||||
* Input type since it too only takes x-y values and is able to get extra info it needs
|
||||
* to be tangential from the previous segment */
|
||||
interface StraightSegmentInput {
|
||||
type: 'straight-segment'
|
||||
from: [number, number]
|
||||
to: [number, number]
|
||||
}
|
||||
|
||||
/**
|
||||
* SegmentInputs is a union type that can be either a StraightSegmentInput or an ArcSegmentInput.
|
||||
*
|
||||
* - StraightSegmentInput: Represents a straight segment with a starting point (from) and an ending point (to).
|
||||
* - ArcSegmentInput: Represents an arc segment with a starting point (from), a center point, and a radius.
|
||||
*/
|
||||
export type SegmentInputs = StraightSegmentInput // TODO ArcSegmentInput
|
||||
|
||||
/**
|
||||
* Interface for adding or replacing a sketch stblib call expression to a sketch.
|
||||
* Replacing normally means adding or removing a constraint
|
||||
*
|
||||
* @property segmentInput - The input segment data, which can be either a straight segment or an arc segment.
|
||||
* @property replaceExistingCallback - An optional callback function to replace an existing call expression,
|
||||
* if not provided, a new call expression will be added using segMentInput values.
|
||||
* @property referencedSegment - An optional path to a referenced segment.
|
||||
* @property spliceBetween=false - Defaults to false. Normal behavior is to add a new callExpression to the end of the pipeExpression.
|
||||
*/
|
||||
interface addCall extends ModifyAstBase {
|
||||
segmentInput: SegmentInputs
|
||||
replaceExistingCallback?: (
|
||||
rawArgs: RawArgs
|
||||
) => CreatedSketchExprResult | Error
|
||||
referencedSegment?: Path
|
||||
replaceExisting?: boolean
|
||||
createCallback?: TransformCallback // TODO: #29 probably should not be optional
|
||||
/// defaults to false, normal behavior is to add a new callExpression to the end of the pipeExpression
|
||||
spliceBetween?: boolean
|
||||
}
|
||||
|
||||
interface updateArgs extends ModifyAstBase {
|
||||
from: [number, number]
|
||||
to: [number, number]
|
||||
input: SegmentInputs
|
||||
}
|
||||
|
||||
export type VarValueKeys = 'angle' | 'offset' | 'length' | 'to' | 'intersectTag'
|
||||
export type InputArgKeys = 'angle' | 'offset' | 'length' | 'to' | 'intersectTag'
|
||||
export interface SingleValueInput<T> {
|
||||
type: 'singleValue'
|
||||
argType: LineInputsType
|
||||
value: T
|
||||
expr: T
|
||||
}
|
||||
export interface ArrayItemInput<T> {
|
||||
type: 'arrayItem'
|
||||
index: 0 | 1
|
||||
argType: LineInputsType
|
||||
value: T
|
||||
expr: T
|
||||
}
|
||||
export interface ObjectPropertyInput<T> {
|
||||
type: 'objectProperty'
|
||||
key: VarValueKeys
|
||||
key: InputArgKeys
|
||||
argType: LineInputsType
|
||||
value: T
|
||||
expr: T
|
||||
}
|
||||
|
||||
export interface ArrayOrObjItemInput<T> {
|
||||
interface ArrayOrObjItemInput<T> {
|
||||
type: 'arrayOrObjItem'
|
||||
key: VarValueKeys
|
||||
key: InputArgKeys
|
||||
index: 0 | 1
|
||||
argType: LineInputsType
|
||||
value: T
|
||||
expr: T
|
||||
}
|
||||
|
||||
export type _VarValue<T> =
|
||||
interface ArrayInObject<T> {
|
||||
type: 'arrayInObject'
|
||||
key: InputArgKeys
|
||||
argType: LineInputsType
|
||||
index: 0 | 1
|
||||
expr: T
|
||||
}
|
||||
|
||||
type _InputArg<T> =
|
||||
| SingleValueInput<T>
|
||||
| ArrayItemInput<T>
|
||||
| ObjectPropertyInput<T>
|
||||
| ArrayOrObjItemInput<T>
|
||||
| ArrayInObject<T>
|
||||
|
||||
export type VarValue = _VarValue<Expr>
|
||||
export type RawValue = _VarValue<Literal>
|
||||
/**
|
||||
* {@link RawArg.expr} is the current expression for each of the args for a segment
|
||||
* i.e. if the expression is 5 + 6, {@link RawArg.expr} will be that binary expression
|
||||
*
|
||||
* Other properties on this type describe how the args are defined for this particular segment
|
||||
* i.e. line uses [x, y] style inputs, while angledLine uses either [angle, length] or {angle, length}
|
||||
* and circle uses {center: [x, y], radius: number}
|
||||
* Which is why a union type is used that can be type narrowed using the {@link RawArg.type} property
|
||||
* {@link RawArg.expr} is common to all of these types
|
||||
*/
|
||||
export type InputArg = _InputArg<Expr>
|
||||
|
||||
export type VarValues = Array<VarValue>
|
||||
export type RawValues = Array<RawValue>
|
||||
/**
|
||||
* {@link RawArg.expr} is the literal equivalent of whatever current expression is
|
||||
* i.e. if the expression is 5 + 6, the literal would be 11
|
||||
* but of course works for expressions like myVar + someFn() etc too
|
||||
* This is useful in cases where we want to "un-constrain" inputs to segments
|
||||
*/
|
||||
type RawArg = _InputArg<Literal>
|
||||
|
||||
type SimplifiedVarValue =
|
||||
| {
|
||||
type: 'singleValue'
|
||||
}
|
||||
| { type: 'arrayItem'; index: 0 | 1 }
|
||||
| { type: 'objectProperty'; key: VarValueKeys }
|
||||
export type InputArgs = Array<InputArg>
|
||||
|
||||
export type TransformCallback = (
|
||||
args: [Expr, Expr],
|
||||
literalValues: RawValues,
|
||||
referencedSegment?: Path
|
||||
) => {
|
||||
/**
|
||||
* The literal equivalent of whatever current expression is
|
||||
* i.e. if the expression is 5 + 6, the literal would be 11
|
||||
* but of course works for expressions like myVar + someFn() etc too
|
||||
* This is useful in cases where we want to "un-constrain" inputs to segments
|
||||
*/
|
||||
export type RawArgs = Array<RawArg>
|
||||
|
||||
/**
|
||||
* Serves the same role as {@link InputArg} on {@link RawArg}
|
||||
* but without the {@link RawArg.expr} property, since it is not needed
|
||||
* when we only need to know where there arg is.
|
||||
*/
|
||||
export type SimplifiedArgDetails =
|
||||
| Omit<SingleValueInput<null>, 'expr' | 'argType'>
|
||||
| Omit<ArrayItemInput<null>, 'expr' | 'argType'>
|
||||
| Omit<ObjectPropertyInput<null>, 'expr' | 'argType'>
|
||||
| Omit<ArrayOrObjItemInput<null>, 'expr' | 'argType'>
|
||||
| Omit<ArrayInObject<null>, 'expr' | 'argType'>
|
||||
/**
|
||||
* Represents the result of creating a sketch expression (line, tangentialArcTo, angledLine, circle, etc.).
|
||||
*
|
||||
* @property {Expr} callExp - This is the main result; recasting the expression should give the user the new function call.
|
||||
* @property {number} [valueUsedInTransform] - Aside from `callExp`, we also return the number used in the transform, which is useful for constraints.
|
||||
* For example, when adding a "horizontal distance" constraint, we don't want the segments to move, just constrain them in place.
|
||||
* So the second segment will probably be something like `lineTo([segEndX($firstSegTag) + someLiteral, 123], %)` where `someLiteral` is
|
||||
* the value of the current horizontal distance, That is we calculate the value needed to constrain the second segment without it moving.
|
||||
* We can run the ast-mod to get this constraint `valueUsedInTransform` without applying the mod so that we can surface this to the user in a modal.
|
||||
* We show them the modal where they can specify the distance they want to constrain to.
|
||||
* We pre-fill this with the current value `valueUsedInTransform`, which they can accept or change, and we'll use that to apply the final ast-mod.
|
||||
*/
|
||||
export interface CreatedSketchExprResult {
|
||||
callExp: Expr
|
||||
valueUsedInTransform?: number
|
||||
}
|
||||
|
||||
export type CreateStdLibSketchCallExpr = (args: {
|
||||
inputs: InputArgs
|
||||
rawArgs: RawArgs
|
||||
referenceSegName: string
|
||||
tag?: Expr
|
||||
forceValueUsedInTransform?: BinaryPart
|
||||
referencedSegment?: Path
|
||||
}) => CreatedSketchExprResult | Error
|
||||
|
||||
export type TransformInfo = {
|
||||
tooltip: ToolTip
|
||||
createNode: CreateStdLibSketchCallExpr
|
||||
}
|
||||
|
||||
export interface ConstrainInfo {
|
||||
stdLibFnName: ToolTip
|
||||
type: LineInputsType | 'vertical' | 'horizontal' | 'tangentialWithPrevious'
|
||||
@ -115,7 +188,7 @@ export interface ConstrainInfo {
|
||||
pathToNode: PathToNode
|
||||
value: string
|
||||
calculatedValue?: any
|
||||
argPosition?: SimplifiedVarValue
|
||||
argPosition?: SimplifiedArgDetails
|
||||
}
|
||||
|
||||
export interface SketchLineHelper {
|
||||
|
@ -20,10 +20,8 @@ import {
|
||||
} from 'lang/queryAst'
|
||||
import { CommandArgument } from './commandTypes'
|
||||
import {
|
||||
STRAIGHT_SEGMENT,
|
||||
TANGENTIAL_ARC_TO_SEGMENT,
|
||||
getParentGroup,
|
||||
PROFILE_START,
|
||||
SEGMENT_BODIES_PLUS_PROFILE_START,
|
||||
} from 'clientSideScene/sceneEntities'
|
||||
import { Mesh, Object3D, Object3DEventMap } from 'three'
|
||||
import { AXIS_GROUP, X_AXIS } from 'clientSideScene/sceneInfra'
|
||||
@ -162,11 +160,7 @@ export async function getEventForSelectWithPoint({
|
||||
export function getEventForSegmentSelection(
|
||||
obj: Object3D<Object3DEventMap>
|
||||
): ModelingMachineEvent | null {
|
||||
const group = getParentGroup(obj, [
|
||||
STRAIGHT_SEGMENT,
|
||||
TANGENTIAL_ARC_TO_SEGMENT,
|
||||
PROFILE_START,
|
||||
])
|
||||
const group = getParentGroup(obj, SEGMENT_BODIES_PLUS_PROFILE_START)
|
||||
const axisGroup = getParentGroup(obj, [AXIS_GROUP])
|
||||
if (!group && !axisGroup) return null
|
||||
if (axisGroup?.userData.type === AXIS_GROUP) {
|
||||
@ -303,12 +297,7 @@ function updateSceneObjectColors(codeBasedSelections: Selection[]) {
|
||||
const updated = kclManager.ast
|
||||
|
||||
Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
|
||||
if (
|
||||
![STRAIGHT_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT, PROFILE_START].includes(
|
||||
segmentGroup?.name
|
||||
)
|
||||
)
|
||||
return
|
||||
if (!SEGMENT_BODIES_PLUS_PROFILE_START.includes(segmentGroup?.name)) return
|
||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||
updated,
|
||||
segmentGroup.userData.pathToNode,
|
||||
|
Reference in New Issue
Block a user