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 { ConstrainInfo } from 'lang/std/stdTypes'
|
||||||
import { getConstraintInfo } from 'lang/std/sketch'
|
import { getConstraintInfo } from 'lang/std/sketch'
|
||||||
import { Dialog, Popover, Transition } from '@headlessui/react'
|
import { Dialog, Popover, Transition } from '@headlessui/react'
|
||||||
import { LineInputsType } from 'lang/std/sketchcombos'
|
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { InstanceProps, create } from 'react-modal-promise'
|
import { InstanceProps, create } from 'react-modal-promise'
|
||||||
import { executeAst } from 'lang/langHelpers'
|
import { executeAst } from 'lang/langHelpers'
|
||||||
import {
|
import {
|
||||||
deleteSegmentFromPipeExpression,
|
deleteSegmentFromPipeExpression,
|
||||||
makeRemoveSingleConstraintInput,
|
|
||||||
removeSingleConstraintInfo,
|
removeSingleConstraintInfo,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import { ActionButton } from 'components/ActionButton'
|
import { ActionButton } from 'components/ActionButton'
|
||||||
@ -542,12 +540,10 @@ const ConstraintSymbol = ({
|
|||||||
iconName: 'dimension',
|
iconName: 'dimension',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
const varName =
|
const varName = varNameMap?.[_type]?.varName || 'var'
|
||||||
_type in varNameMap ? varNameMap[_type as LineInputsType].varName : 'var'
|
const name: CustomIconName = varNameMap[_type].iconName
|
||||||
const name: CustomIconName = varNameMap[_type as LineInputsType].iconName
|
const displayName = varNameMap[_type]?.displayName
|
||||||
const displayName = varNameMap[_type as LineInputsType]?.displayName
|
const implicitDesc = varNameMap[_type]?.implicitConstraintDesc
|
||||||
const implicitDesc =
|
|
||||||
varNameMap[_type as LineInputsType]?.implicitConstraintDesc
|
|
||||||
|
|
||||||
const _node = useMemo(
|
const _node = useMemo(
|
||||||
() => getNodeFromPath<Expr>(kclManager.ast, pathToNode),
|
() => getNodeFromPath<Expr>(kclManager.ast, pathToNode),
|
||||||
@ -604,13 +600,10 @@ const ConstraintSymbol = ({
|
|||||||
if (trap(_node1)) return Promise.reject(_node1)
|
if (trap(_node1)) return Promise.reject(_node1)
|
||||||
const shallowPath = _node1.shallowPath
|
const shallowPath = _node1.shallowPath
|
||||||
|
|
||||||
const input = makeRemoveSingleConstraintInput(
|
if (!context.sketchDetails || !argPosition) return
|
||||||
argPosition,
|
|
||||||
shallowPath
|
|
||||||
)
|
|
||||||
if (!input || !context.sketchDetails) return
|
|
||||||
const transform = removeSingleConstraintInfo(
|
const transform = removeSingleConstraintInfo(
|
||||||
input,
|
shallowPath,
|
||||||
|
argPosition,
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
kclManager.programMemory
|
kclManager.programMemory
|
||||||
)
|
)
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
BoxGeometry,
|
BoxGeometry,
|
||||||
DoubleSide,
|
DoubleSide,
|
||||||
ExtrudeGeometry,
|
|
||||||
Group,
|
Group,
|
||||||
Intersection,
|
Intersection,
|
||||||
LineCurve3,
|
|
||||||
Mesh,
|
Mesh,
|
||||||
MeshBasicMaterial,
|
MeshBasicMaterial,
|
||||||
Object3D,
|
Object3D,
|
||||||
@ -15,7 +13,6 @@ import {
|
|||||||
Points,
|
Points,
|
||||||
Quaternion,
|
Quaternion,
|
||||||
Scene,
|
Scene,
|
||||||
Shape,
|
|
||||||
Vector2,
|
Vector2,
|
||||||
Vector3,
|
Vector3,
|
||||||
} from 'three'
|
} from 'three'
|
||||||
@ -27,8 +24,6 @@ import {
|
|||||||
OnClickCallbackArgs,
|
OnClickCallbackArgs,
|
||||||
OnMouseEnterLeaveArgs,
|
OnMouseEnterLeaveArgs,
|
||||||
RAYCASTABLE_PLANE,
|
RAYCASTABLE_PLANE,
|
||||||
SEGMENT_LENGTH_LABEL,
|
|
||||||
SEGMENT_LENGTH_LABEL_TEXT,
|
|
||||||
SKETCH_GROUP_SEGMENTS,
|
SKETCH_GROUP_SEGMENTS,
|
||||||
SKETCH_LAYER,
|
SKETCH_LAYER,
|
||||||
X_AXIS,
|
X_AXIS,
|
||||||
@ -37,7 +32,6 @@ import {
|
|||||||
import { isQuaternionVertical, quaternionFromUpNForward } from './helpers'
|
import { isQuaternionVertical, quaternionFromUpNForward } from './helpers'
|
||||||
import {
|
import {
|
||||||
CallExpression,
|
CallExpression,
|
||||||
getTangentialArcToInfo,
|
|
||||||
parse,
|
parse,
|
||||||
Path,
|
Path,
|
||||||
PathToNode,
|
PathToNode,
|
||||||
@ -61,11 +55,9 @@ import {
|
|||||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { executeAst } from 'lang/langHelpers'
|
import { executeAst } from 'lang/langHelpers'
|
||||||
import {
|
import {
|
||||||
createArcGeometry,
|
createProfileStartHandle,
|
||||||
dashedStraight,
|
SegmentUtils,
|
||||||
profileStart,
|
segmentUtils,
|
||||||
straightSegment,
|
|
||||||
tangentialArcToSegment,
|
|
||||||
} from './segments'
|
} from './segments'
|
||||||
import {
|
import {
|
||||||
addCallExpressionsToPipe,
|
addCallExpressionsToPipe,
|
||||||
@ -74,13 +66,7 @@ import {
|
|||||||
changeSketchArguments,
|
changeSketchArguments,
|
||||||
updateStartProfileAtArgs,
|
updateStartProfileAtArgs,
|
||||||
} from 'lang/std/sketch'
|
} from 'lang/std/sketch'
|
||||||
import {
|
import { isArray, isOverlap, roundOff } from 'lib/utils'
|
||||||
isArray,
|
|
||||||
isOverlap,
|
|
||||||
normaliseAngle,
|
|
||||||
roundOff,
|
|
||||||
throttle,
|
|
||||||
} from 'lib/utils'
|
|
||||||
import {
|
import {
|
||||||
addStartProfileAt,
|
addStartProfileAt,
|
||||||
createArrayExpression,
|
createArrayExpression,
|
||||||
@ -91,7 +77,6 @@ import {
|
|||||||
findUniqueName,
|
findUniqueName,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import { Selections, getEventForSegmentSelection } from 'lib/selections'
|
import { Selections, getEventForSegmentSelection } from 'lib/selections'
|
||||||
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
|
||||||
import { createGridHelper, orthoScale, perspScale } from './helpers'
|
import { createGridHelper, orthoScale, perspScale } from './helpers'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { uuidv4 } from 'lib/utils'
|
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 SEGMENT_WIDTH_PX = 1.6
|
||||||
export const HIDE_SEGMENT_LENGTH = 75 // in pixels
|
export const HIDE_SEGMENT_LENGTH = 75 // in pixels
|
||||||
export const HIDE_HOVER_SEGMENT_LENGTH = 60 // 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]
|
type Vec3Array = [number, number, number]
|
||||||
|
|
||||||
@ -154,37 +144,35 @@ export class SceneEntities {
|
|||||||
? orthoFactor
|
? orthoFactor
|
||||||
: perspScale(sceneInfra.camControls.camera, segment)) /
|
: perspScale(sceneInfra.camControls.camera, segment)) /
|
||||||
sceneInfra._baseUnitMultiplier
|
sceneInfra._baseUnitMultiplier
|
||||||
|
const input = {
|
||||||
|
type: 'straight-segment',
|
||||||
|
from: segment.userData.from,
|
||||||
|
to: segment.userData.to,
|
||||||
|
} as const
|
||||||
|
let update: SegmentUtils['update'] | null = null
|
||||||
if (
|
if (
|
||||||
segment.userData.from &&
|
segment.userData.from &&
|
||||||
segment.userData.to &&
|
segment.userData.to &&
|
||||||
segment.userData.type === STRAIGHT_SEGMENT
|
segment.userData.type === STRAIGHT_SEGMENT
|
||||||
) {
|
) {
|
||||||
callbacks.push(
|
update = segmentUtils.straight.update
|
||||||
this.updateStraightSegment({
|
|
||||||
from: segment.userData.from,
|
|
||||||
to: segment.userData.to,
|
|
||||||
group: segment,
|
|
||||||
scale: factor,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
segment.userData.from &&
|
segment.userData.from &&
|
||||||
segment.userData.to &&
|
segment.userData.to &&
|
||||||
segment.userData.prevSegment &&
|
segment.userData.prevSegment &&
|
||||||
segment.userData.type === TANGENTIAL_ARC_TO_SEGMENT
|
segment.userData.type === TANGENTIAL_ARC_TO_SEGMENT
|
||||||
) {
|
) {
|
||||||
callbacks.push(
|
update = segmentUtils.tangentialArcTo.update
|
||||||
this.updateTangentialArcToSegment({
|
|
||||||
prevSegment: segment.userData.prevSegment,
|
|
||||||
from: segment.userData.from,
|
|
||||||
to: segment.userData.to,
|
|
||||||
group: segment,
|
|
||||||
scale: factor,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
const callBack = update?.({
|
||||||
|
prevSegment: segment.userData.prevSegment,
|
||||||
|
input,
|
||||||
|
group: segment,
|
||||||
|
scale: factor,
|
||||||
|
sceneInfra,
|
||||||
|
})
|
||||||
|
callBack && !err(callBack) && callbacks.push(callBack)
|
||||||
if (segment.name === PROFILE_START) {
|
if (segment.name === PROFILE_START) {
|
||||||
segment.scale.set(factor, factor, factor)
|
segment.scale.set(factor, factor, factor)
|
||||||
}
|
}
|
||||||
@ -421,7 +409,7 @@ export class SceneEntities {
|
|||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
sketchGroup.start.__geoMeta.sourceRange
|
sketchGroup.start.__geoMeta.sourceRange
|
||||||
)
|
)
|
||||||
const _profileStart = profileStart({
|
const _profileStart = createProfileStartHandle({
|
||||||
from: sketchGroup.start.from,
|
from: sketchGroup.start.from,
|
||||||
id: sketchGroup.start.__geoMeta.id,
|
id: sketchGroup.start.__geoMeta.id,
|
||||||
pathToNode: segPathToNode,
|
pathToNode: segPathToNode,
|
||||||
@ -476,50 +464,31 @@ export class SceneEntities {
|
|||||||
if (err(_node1)) return
|
if (err(_node1)) return
|
||||||
const callExpName = _node1.node?.callee?.name
|
const callExpName = _node1.node?.callee?.name
|
||||||
|
|
||||||
if (segment.type === 'TangentialArcTo') {
|
const initSegment =
|
||||||
seg = tangentialArcToSegment({
|
segment.type === 'TangentialArcTo'
|
||||||
prevSegment: sketchGroup.value[index - 1],
|
? segmentUtils.tangentialArcTo.init
|
||||||
|
: segmentUtils.straight.init
|
||||||
|
const result = initSegment({
|
||||||
|
prevSegment: sketchGroup.value[index - 1],
|
||||||
|
callExpName,
|
||||||
|
input: {
|
||||||
|
type: 'straight-segment',
|
||||||
from: segment.from,
|
from: segment.from,
|
||||||
to: segment.to,
|
to: segment.to,
|
||||||
id: segment.__geoMeta.id,
|
},
|
||||||
pathToNode: segPathToNode,
|
id: segment.__geoMeta.id,
|
||||||
isDraftSegment,
|
pathToNode: segPathToNode,
|
||||||
scale: factor,
|
isDraftSegment,
|
||||||
texture: sceneInfra.extraSegmentTexture,
|
scale: factor,
|
||||||
theme: sceneInfra._theme,
|
texture: sceneInfra.extraSegmentTexture,
|
||||||
isSelected,
|
theme: sceneInfra._theme,
|
||||||
})
|
isSelected,
|
||||||
callbacks.push(
|
sceneInfra,
|
||||||
this.updateTangentialArcToSegment({
|
})
|
||||||
prevSegment: sketchGroup.value[index - 1],
|
if (err(result)) return
|
||||||
from: segment.from,
|
const { group: _group, updateOverlaysCallback } = result
|
||||||
to: segment.to,
|
seg = _group
|
||||||
group: seg,
|
callbacks.push(updateOverlaysCallback)
|
||||||
scale: factor,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
seg = straightSegment({
|
|
||||||
from: segment.from,
|
|
||||||
to: segment.to,
|
|
||||||
id: segment.__geoMeta.id,
|
|
||||||
pathToNode: segPathToNode,
|
|
||||||
isDraftSegment,
|
|
||||||
scale: factor,
|
|
||||||
callExpName,
|
|
||||||
texture: sceneInfra.extraSegmentTexture,
|
|
||||||
theme: sceneInfra._theme,
|
|
||||||
isSelected,
|
|
||||||
})
|
|
||||||
callbacks.push(
|
|
||||||
this.updateStraightSegment({
|
|
||||||
from: segment.from,
|
|
||||||
to: segment.to,
|
|
||||||
group: seg,
|
|
||||||
scale: factor,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
seg.layers.set(SKETCH_LAYER)
|
seg.layers.set(SKETCH_LAYER)
|
||||||
seg.traverse((child) => {
|
seg.traverse((child) => {
|
||||||
child.layers.set(SKETCH_LAYER)
|
child.layers.set(SKETCH_LAYER)
|
||||||
@ -602,16 +571,19 @@ export class SceneEntities {
|
|||||||
kclManager.programMemory.get(variableDeclarationName),
|
kclManager.programMemory.get(variableDeclarationName),
|
||||||
variableDeclarationName
|
variableDeclarationName
|
||||||
)
|
)
|
||||||
if (err(sg)) return sg
|
if (err(sg)) return Promise.reject(sg)
|
||||||
const lastSeg = sg.value?.slice(-1)[0] || sg.start
|
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 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({
|
const mod = addNewSketchLn({
|
||||||
node: _ast,
|
node: _ast,
|
||||||
programMemory: kclManager.programMemory,
|
programMemory: kclManager.programMemory,
|
||||||
to: [lastSeg.to[0], lastSeg.to[1]],
|
input: {
|
||||||
from: [lastSeg.to[0], lastSeg.to[1]],
|
type: 'straight-segment',
|
||||||
|
to: lastSeg.to,
|
||||||
|
from: lastSeg.to,
|
||||||
|
},
|
||||||
fnName: segmentName,
|
fnName: segmentName,
|
||||||
pathToNode: sketchPathToNode,
|
pathToNode: sketchPathToNode,
|
||||||
})
|
})
|
||||||
@ -682,8 +654,11 @@ export class SceneEntities {
|
|||||||
const tmp = addNewSketchLn({
|
const tmp = addNewSketchLn({
|
||||||
node: kclManager.ast,
|
node: kclManager.ast,
|
||||||
programMemory: kclManager.programMemory,
|
programMemory: kclManager.programMemory,
|
||||||
to: [intersection2d.x, intersection2d.y],
|
input: {
|
||||||
from: [lastSegment.to[0], lastSegment.to[1]],
|
type: 'straight-segment',
|
||||||
|
from: [lastSegment.to[0], lastSegment.to[1]],
|
||||||
|
to: [intersection2d.x, intersection2d.y],
|
||||||
|
},
|
||||||
fnName:
|
fnName:
|
||||||
lastSegment.type === 'TangentialArcTo'
|
lastSegment.type === 'TangentialArcTo'
|
||||||
? 'tangentialArcTo'
|
? 'tangentialArcTo'
|
||||||
@ -834,14 +809,14 @@ export class SceneEntities {
|
|||||||
sketchPathToNode || [],
|
sketchPathToNode || [],
|
||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node)) return Promise.reject(_node)
|
if (trap(_node)) return
|
||||||
const sketchInit = _node.node?.declarations?.[0]?.init
|
const sketchInit = _node.node?.declarations?.[0]?.init
|
||||||
|
|
||||||
if (sketchInit.type === 'PipeExpression') {
|
if (sketchInit.type === 'PipeExpression') {
|
||||||
updateRectangleSketch(sketchInit, x, y, tags[0])
|
updateRectangleSketch(sketchInit, x, y, tags[0])
|
||||||
|
|
||||||
let _recastAst = parse(recast(_ast))
|
let _recastAst = parse(recast(_ast))
|
||||||
if (trap(_recastAst)) return Promise.reject(_recastAst)
|
if (trap(_recastAst)) return
|
||||||
_ast = _recastAst
|
_ast = _recastAst
|
||||||
|
|
||||||
// Update the primary AST and unequip the rectangle tool
|
// Update the primary AST and unequip the rectangle tool
|
||||||
@ -861,7 +836,7 @@ export class SceneEntities {
|
|||||||
programMemory.get(variableDeclarationName),
|
programMemory.get(variableDeclarationName),
|
||||||
variableDeclarationName
|
variableDeclarationName
|
||||||
)
|
)
|
||||||
if (err(sketchGroup)) return sketchGroup
|
if (err(sketchGroup)) return
|
||||||
const sgPaths = sketchGroup.value
|
const sgPaths = sketchGroup.value
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
|
|
||||||
@ -950,8 +925,11 @@ export class SceneEntities {
|
|||||||
const mod = addNewSketchLn({
|
const mod = addNewSketchLn({
|
||||||
node: kclManager.ast,
|
node: kclManager.ast,
|
||||||
programMemory: kclManager.programMemory,
|
programMemory: kclManager.programMemory,
|
||||||
to: [intersectionPoint.twoD.x, intersectionPoint.twoD.y],
|
input: {
|
||||||
from: [prevSegment.from[0], prevSegment.from[1]],
|
type: 'straight-segment',
|
||||||
|
to: [intersectionPoint.twoD.x, intersectionPoint.twoD.y],
|
||||||
|
from: prevSegment.from,
|
||||||
|
},
|
||||||
// TODO assuming it's always a straight segments being added
|
// TODO assuming it's always a straight segments being added
|
||||||
// as this is easiest, and we'll need to add "tabbing" behavior
|
// as this is easiest, and we'll need to add "tabbing" behavior
|
||||||
// to support other segment types
|
// to support other segment types
|
||||||
@ -1072,7 +1050,7 @@ export class SceneEntities {
|
|||||||
group.userData.from[0],
|
group.userData.from[0],
|
||||||
group.userData.from[1],
|
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 }
|
let modifiedAst = draftInfo ? draftInfo.truncatedAst : { ...kclManager.ast }
|
||||||
|
|
||||||
const _node = getNodeFromPath<CallExpression>(
|
const _node = getNodeFromPath<CallExpression>(
|
||||||
@ -1095,8 +1073,11 @@ export class SceneEntities {
|
|||||||
modded = updateStartProfileAtArgs({
|
modded = updateStartProfileAtArgs({
|
||||||
node: modifiedAst,
|
node: modifiedAst,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
to,
|
input: {
|
||||||
from,
|
type: 'straight-segment',
|
||||||
|
to: dragTo,
|
||||||
|
from,
|
||||||
|
},
|
||||||
previousProgramMemory: kclManager.programMemory,
|
previousProgramMemory: kclManager.programMemory,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -1104,8 +1085,11 @@ export class SceneEntities {
|
|||||||
modifiedAst,
|
modifiedAst,
|
||||||
kclManager.programMemory,
|
kclManager.programMemory,
|
||||||
[node.start, node.end],
|
[node.start, node.end],
|
||||||
to,
|
{
|
||||||
from
|
type: 'straight-segment',
|
||||||
|
from,
|
||||||
|
to: dragTo,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (trap(modded)) return
|
if (trap(modded)) return
|
||||||
@ -1208,264 +1192,36 @@ export class SceneEntities {
|
|||||||
? orthoFactor
|
? orthoFactor
|
||||||
: perspScale(sceneInfra.camControls.camera, group)) /
|
: perspScale(sceneInfra.camControls.camera, group)) /
|
||||||
sceneInfra._baseUnitMultiplier
|
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) {
|
if (type === TANGENTIAL_ARC_TO_SEGMENT) {
|
||||||
return this.updateTangentialArcToSegment({
|
update = segmentUtils.tangentialArcTo.update
|
||||||
prevSegment: sgPaths[index - 1],
|
|
||||||
from: segment.from,
|
|
||||||
to: segment.to,
|
|
||||||
group: group,
|
|
||||||
scale: factor,
|
|
||||||
})
|
|
||||||
} else if (type === STRAIGHT_SEGMENT) {
|
} else if (type === STRAIGHT_SEGMENT) {
|
||||||
return this.updateStraightSegment({
|
update = segmentUtils.straight.update
|
||||||
from: segment.from,
|
}
|
||||||
to: segment.to,
|
const callBack =
|
||||||
|
update &&
|
||||||
|
!err(update) &&
|
||||||
|
update({
|
||||||
|
input,
|
||||||
group,
|
group,
|
||||||
scale: factor,
|
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.position.set(segment.from[0], segment.from[1], 0)
|
||||||
group.scale.set(factor, factor, factor)
|
group.scale.set(factor, factor, factor)
|
||||||
}
|
}
|
||||||
return () => null
|
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
|
* Update the base color of each of the THREEjs meshes
|
||||||
* that represent each of the sketch segments, to get the
|
* that represent each of the sketch segments, to get the
|
||||||
@ -1578,27 +1334,30 @@ export class SceneEntities {
|
|||||||
}
|
}
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
|
|
||||||
|
const input = {
|
||||||
|
type: 'straight-segment',
|
||||||
|
from: parent.userData.from,
|
||||||
|
to: parent.userData.to,
|
||||||
|
} as const
|
||||||
const factor =
|
const factor =
|
||||||
(sceneInfra.camControls.camera instanceof OrthographicCamera
|
(sceneInfra.camControls.camera instanceof OrthographicCamera
|
||||||
? orthoFactor
|
? orthoFactor
|
||||||
: perspScale(sceneInfra.camControls.camera, parent)) /
|
: perspScale(sceneInfra.camControls.camera, parent)) /
|
||||||
sceneInfra._baseUnitMultiplier
|
sceneInfra._baseUnitMultiplier
|
||||||
|
let update: SegmentUtils['update'] | null = null
|
||||||
if (parent.name === STRAIGHT_SEGMENT) {
|
if (parent.name === STRAIGHT_SEGMENT) {
|
||||||
this.updateStraightSegment({
|
update = segmentUtils.straight.update
|
||||||
from: parent.userData.from,
|
|
||||||
to: parent.userData.to,
|
|
||||||
group: parent,
|
|
||||||
scale: factor,
|
|
||||||
})
|
|
||||||
} else if (parent.name === TANGENTIAL_ARC_TO_SEGMENT) {
|
} else if (parent.name === TANGENTIAL_ARC_TO_SEGMENT) {
|
||||||
this.updateTangentialArcToSegment({
|
update = segmentUtils.tangentialArcTo.update
|
||||||
|
}
|
||||||
|
update &&
|
||||||
|
update({
|
||||||
prevSegment: parent.userData.prevSegment,
|
prevSegment: parent.userData.prevSegment,
|
||||||
from: parent.userData.from,
|
input,
|
||||||
to: parent.userData.to,
|
|
||||||
group: parent,
|
group: parent,
|
||||||
scale: factor,
|
scale: factor,
|
||||||
|
sceneInfra,
|
||||||
})
|
})
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
editorManager.setHighlightRange([[0, 0]])
|
editorManager.setHighlightRange([[0, 0]])
|
||||||
@ -1613,27 +1372,30 @@ export class SceneEntities {
|
|||||||
if (parent) {
|
if (parent) {
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
|
|
||||||
|
const input = {
|
||||||
|
type: 'straight-segment',
|
||||||
|
from: parent.userData.from,
|
||||||
|
to: parent.userData.to,
|
||||||
|
} as const
|
||||||
const factor =
|
const factor =
|
||||||
(sceneInfra.camControls.camera instanceof OrthographicCamera
|
(sceneInfra.camControls.camera instanceof OrthographicCamera
|
||||||
? orthoFactor
|
? orthoFactor
|
||||||
: perspScale(sceneInfra.camControls.camera, parent)) /
|
: perspScale(sceneInfra.camControls.camera, parent)) /
|
||||||
sceneInfra._baseUnitMultiplier
|
sceneInfra._baseUnitMultiplier
|
||||||
|
let update: SegmentUtils['update'] | null = null
|
||||||
if (parent.name === STRAIGHT_SEGMENT) {
|
if (parent.name === STRAIGHT_SEGMENT) {
|
||||||
this.updateStraightSegment({
|
update = segmentUtils.straight.update
|
||||||
from: parent.userData.from,
|
|
||||||
to: parent.userData.to,
|
|
||||||
group: parent,
|
|
||||||
scale: factor,
|
|
||||||
})
|
|
||||||
} else if (parent.name === TANGENTIAL_ARC_TO_SEGMENT) {
|
} else if (parent.name === TANGENTIAL_ARC_TO_SEGMENT) {
|
||||||
this.updateTangentialArcToSegment({
|
update = segmentUtils.tangentialArcTo.update
|
||||||
|
}
|
||||||
|
update &&
|
||||||
|
update({
|
||||||
prevSegment: parent.userData.prevSegment,
|
prevSegment: parent.userData.prevSegment,
|
||||||
from: parent.userData.from,
|
input,
|
||||||
to: parent.userData.to,
|
|
||||||
group: parent,
|
group: parent,
|
||||||
scale: factor,
|
scale: factor,
|
||||||
|
sceneInfra,
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const isSelected = parent?.userData?.isSelected
|
const isSelected = parent?.userData?.isSelected
|
||||||
colorSegment(
|
colorSegment(
|
||||||
|
@ -26,6 +26,7 @@ import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm'
|
|||||||
import {
|
import {
|
||||||
EXTRA_SEGMENT_HANDLE,
|
EXTRA_SEGMENT_HANDLE,
|
||||||
EXTRA_SEGMENT_OFFSET_PX,
|
EXTRA_SEGMENT_OFFSET_PX,
|
||||||
|
HIDE_HOVER_SEGMENT_LENGTH,
|
||||||
HIDE_SEGMENT_LENGTH,
|
HIDE_SEGMENT_LENGTH,
|
||||||
PROFILE_START,
|
PROFILE_START,
|
||||||
SEGMENT_WIDTH_PX,
|
SEGMENT_WIDTH_PX,
|
||||||
@ -35,18 +36,448 @@ import {
|
|||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
TANGENTIAL_ARC_TO_SEGMENT,
|
||||||
TANGENTIAL_ARC_TO_SEGMENT_BODY,
|
TANGENTIAL_ARC_TO_SEGMENT_BODY,
|
||||||
TANGENTIAL_ARC_TO__SEGMENT_DASH,
|
TANGENTIAL_ARC_TO__SEGMENT_DASH,
|
||||||
|
getParentGroup,
|
||||||
} from './sceneEntities'
|
} from './sceneEntities'
|
||||||
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
||||||
import {
|
import {
|
||||||
ARROWHEAD,
|
ARROWHEAD,
|
||||||
|
SceneInfra,
|
||||||
SEGMENT_LENGTH_LABEL,
|
SEGMENT_LENGTH_LABEL,
|
||||||
SEGMENT_LENGTH_LABEL_OFFSET_PX,
|
SEGMENT_LENGTH_LABEL_OFFSET_PX,
|
||||||
SEGMENT_LENGTH_LABEL_TEXT,
|
SEGMENT_LENGTH_LABEL_TEXT,
|
||||||
} from './sceneInfra'
|
} from './sceneInfra'
|
||||||
import { Themes, getThemeColorForThreeJs } from 'lib/theme'
|
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,
|
from,
|
||||||
id,
|
id,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
@ -85,127 +516,6 @@ export function profileStart({
|
|||||||
return group
|
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 {
|
function createArrowhead(scale = 1, theme: Themes, color?: number): Group {
|
||||||
const baseColor = getThemeColorForThreeJs(theme)
|
const baseColor = getThemeColorForThreeJs(theme)
|
||||||
const arrowMaterial = new MeshBasicMaterial({
|
const arrowMaterial = new MeshBasicMaterial({
|
||||||
@ -267,12 +577,12 @@ function createLengthIndicator({
|
|||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
scale,
|
scale,
|
||||||
length,
|
length = 0.1,
|
||||||
}: {
|
}: {
|
||||||
from: Coords2d
|
from: Coords2d
|
||||||
to: Coords2d
|
to: Coords2d
|
||||||
scale: number
|
scale: number
|
||||||
length: number
|
length?: number
|
||||||
}) {
|
}) {
|
||||||
const lengthIndicatorGroup = new Group()
|
const lengthIndicatorGroup = new Group()
|
||||||
lengthIndicatorGroup.name = SEGMENT_LENGTH_LABEL
|
lengthIndicatorGroup.name = SEGMENT_LENGTH_LABEL
|
||||||
@ -300,111 +610,6 @@ function createLengthIndicator({
|
|||||||
return lengthIndicatorGroup
|
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({
|
export function createArcGeometry({
|
||||||
center,
|
center,
|
||||||
radius,
|
radius,
|
||||||
@ -579,3 +784,8 @@ export function dashedStraight(
|
|||||||
geo.userData.type = 'dashed'
|
geo.userData.type = 'dashed'
|
||||||
return geo
|
return geo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const segmentUtils = {
|
||||||
|
straight: new StraightSegment(),
|
||||||
|
tangentialArcTo: new TangentialArcToSegment(),
|
||||||
|
} as const
|
||||||
|
@ -204,6 +204,7 @@ export const ModelingMachineProvider = ({
|
|||||||
pathToNodeString,
|
pathToNodeString,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
// overlay timeout
|
||||||
}, 800) as unknown as number
|
}, 800) as unknown as number
|
||||||
return {
|
return {
|
||||||
...context.segmentHoverMap,
|
...context.segmentHoverMap,
|
||||||
|
@ -10,10 +10,10 @@ import {
|
|||||||
transformSecondarySketchLinesTagFirst,
|
transformSecondarySketchLinesTagFirst,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
TransformInfo,
|
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
|
|
||||||
export function equalAngleInfo({
|
export function equalAngleInfo({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
|
@ -10,8 +10,8 @@ import {
|
|||||||
transformSecondarySketchLinesTagFirst,
|
transformSecondarySketchLinesTagFirst,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
TransformInfo,
|
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ import {
|
|||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
transformAstSketchLines,
|
transformAstSketchLines,
|
||||||
TransformInfo,
|
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { toolTips } from 'lang/langHelpers'
|
import { toolTips } from 'lang/langHelpers'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { BinaryPart, Program, Expr, VariableDeclarator } from '../../lang/wasm'
|
import { Program, Expr, VariableDeclarator } from '../../lang/wasm'
|
||||||
import {
|
import {
|
||||||
getNodePathFromSourceRange,
|
getNodePathFromSourceRange,
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
@ -11,8 +11,9 @@ import {
|
|||||||
transformSecondarySketchLinesTagFirst,
|
transformSecondarySketchLinesTagFirst,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
TransformInfo,
|
isExprBinaryPart,
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||||
import { createVariableDeclaration } from '../../lang/modifyAst'
|
import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
@ -178,11 +179,9 @@ export async function applyConstraintIntersect({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// transform again but forcing certain values
|
// transform again but forcing certain values
|
||||||
const finalValue = removeDoubleNegatives(
|
if (!isExprBinaryPart(valueNode))
|
||||||
valueNode as BinaryPart,
|
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||||
sign,
|
const finalValue = removeDoubleNegatives(valueNode, sign, variableName)
|
||||||
variableName
|
|
||||||
)
|
|
||||||
const transform2 = transformSecondarySketchLinesTagFirst({
|
const transform2 = transformSecondarySketchLinesTagFirst({
|
||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
selectionRanges: forcedSelectionRanges,
|
selectionRanges: forcedSelectionRanges,
|
||||||
|
@ -9,8 +9,8 @@ import {
|
|||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
getRemoveConstraintsTransforms,
|
getRemoveConstraintsTransforms,
|
||||||
transformAstSketchLines,
|
transformAstSketchLines,
|
||||||
TransformInfo,
|
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { toolTips } from 'lang/langHelpers'
|
import { toolTips } from 'lang/langHelpers'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { BinaryPart, Program, Expr } from '../../lang/wasm'
|
import { Program, Expr } from '../../lang/wasm'
|
||||||
import {
|
import {
|
||||||
getNodePathFromSourceRange,
|
getNodePathFromSourceRange,
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
@ -9,8 +9,9 @@ import {
|
|||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
transformAstSketchLines,
|
transformAstSketchLines,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
TransformInfo,
|
isExprBinaryPart,
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import {
|
import {
|
||||||
SetAngleLengthModal,
|
SetAngleLengthModal,
|
||||||
createSetAngleLengthModal,
|
createSetAngleLengthModal,
|
||||||
@ -121,11 +122,9 @@ export async function applyConstraintAbsDistance({
|
|||||||
value: forceVal,
|
value: forceVal,
|
||||||
valueName: constraint === 'yAbs' ? 'yDis' : 'xDis',
|
valueName: constraint === 'yAbs' ? 'yDis' : 'xDis',
|
||||||
})
|
})
|
||||||
let finalValue = removeDoubleNegatives(
|
if (!isExprBinaryPart(valueNode))
|
||||||
valueNode as BinaryPart,
|
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||||
sign,
|
let finalValue = removeDoubleNegatives(valueNode, sign, variableName)
|
||||||
variableName
|
|
||||||
)
|
|
||||||
|
|
||||||
const transform2 = transformAstSketchLines({
|
const transform2 = transformAstSketchLines({
|
||||||
ast: structuredClone(kclManager.ast),
|
ast: structuredClone(kclManager.ast),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { toolTips } from 'lang/langHelpers'
|
import { toolTips } from 'lang/langHelpers'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { BinaryPart, Program, Expr, VariableDeclarator } from '../../lang/wasm'
|
import { Program, Expr, VariableDeclarator } from '../../lang/wasm'
|
||||||
import {
|
import {
|
||||||
getNodePathFromSourceRange,
|
getNodePathFromSourceRange,
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
@ -10,8 +10,9 @@ import {
|
|||||||
transformSecondarySketchLinesTagFirst,
|
transformSecondarySketchLinesTagFirst,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
TransformInfo,
|
isExprBinaryPart,
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||||
import { createVariableDeclaration } from '../../lang/modifyAst'
|
import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
@ -133,11 +134,9 @@ export async function applyConstraintAngleBetween({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const finalValue = removeDoubleNegatives(
|
if (!isExprBinaryPart(valueNode))
|
||||||
valueNode as BinaryPart,
|
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||||
sign,
|
const finalValue = removeDoubleNegatives(valueNode, sign, variableName)
|
||||||
variableName
|
|
||||||
)
|
|
||||||
// transform again but forcing certain values
|
// transform again but forcing certain values
|
||||||
const transformed2 = transformSecondarySketchLinesTagFirst({
|
const transformed2 = transformSecondarySketchLinesTagFirst({
|
||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { toolTips } from 'lang/langHelpers'
|
import { toolTips } from 'lang/langHelpers'
|
||||||
import { BinaryPart, Program, Expr, VariableDeclarator } from '../../lang/wasm'
|
import { Program, Expr, VariableDeclarator } from '../../lang/wasm'
|
||||||
import {
|
import {
|
||||||
getNodePathFromSourceRange,
|
getNodePathFromSourceRange,
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
@ -9,8 +9,9 @@ import {
|
|||||||
transformSecondarySketchLinesTagFirst,
|
transformSecondarySketchLinesTagFirst,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
TransformInfo,
|
isExprBinaryPart,
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||||
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
|
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
@ -139,9 +140,11 @@ export async function applyConstraintHorzVertDistance({
|
|||||||
pathToNodeMap,
|
pathToNodeMap,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (!isExprBinaryPart(valueNode))
|
||||||
|
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||||
let finalValue = isAlign
|
let finalValue = isAlign
|
||||||
? createLiteral(0)
|
? createLiteral(0)
|
||||||
: removeDoubleNegatives(valueNode as BinaryPart, sign, variableName)
|
: removeDoubleNegatives(valueNode, sign, variableName)
|
||||||
// transform again but forcing certain values
|
// transform again but forcing certain values
|
||||||
const transformed = transformSecondarySketchLinesTagFirst({
|
const transformed = transformSecondarySketchLinesTagFirst({
|
||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { toolTips } from 'lang/langHelpers'
|
import { toolTips } from 'lang/langHelpers'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { BinaryPart, Program, Expr } from '../../lang/wasm'
|
import { Program, Expr } from '../../lang/wasm'
|
||||||
import {
|
import {
|
||||||
getNodePathFromSourceRange,
|
getNodePathFromSourceRange,
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
@ -8,9 +8,10 @@ import {
|
|||||||
import {
|
import {
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
|
isExprBinaryPart,
|
||||||
transformAstSketchLines,
|
transformAstSketchLines,
|
||||||
TransformInfo,
|
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import {
|
import {
|
||||||
SetAngleLengthModal,
|
SetAngleLengthModal,
|
||||||
createSetAngleLengthModal,
|
createSetAngleLengthModal,
|
||||||
@ -125,12 +126,9 @@ export async function applyConstraintAngleLength({
|
|||||||
valueName: angleOrLength === 'setAngle' ? 'angle' : 'length',
|
valueName: angleOrLength === 'setAngle' ? 'angle' : 'length',
|
||||||
shouldCreateVariable: true,
|
shouldCreateVariable: true,
|
||||||
})
|
})
|
||||||
|
if (!isExprBinaryPart(valueNode))
|
||||||
let finalValue = removeDoubleNegatives(
|
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||||
valueNode as BinaryPart,
|
let finalValue = removeDoubleNegatives(valueNode, sign, variableName)
|
||||||
sign,
|
|
||||||
variableName
|
|
||||||
)
|
|
||||||
if (
|
if (
|
||||||
isReferencingYAxisAngle ||
|
isReferencingYAxisAngle ||
|
||||||
(isReferencingXAxisAngle && calcIdentifier.name !== 'ZERO')
|
(isReferencingXAxisAngle && calcIdentifier.name !== 'ZERO')
|
||||||
|
@ -26,9 +26,6 @@ export type ToolTip =
|
|||||||
| 'tangentialArcTo'
|
| 'tangentialArcTo'
|
||||||
|
|
||||||
export const toolTips = [
|
export const toolTips = [
|
||||||
'sketch_line',
|
|
||||||
'move',
|
|
||||||
// original tooltips
|
|
||||||
'line',
|
'line',
|
||||||
'lineTo',
|
'lineTo',
|
||||||
'angledLine',
|
'angledLine',
|
||||||
@ -42,7 +39,7 @@ export const toolTips = [
|
|||||||
'yLineTo',
|
'yLineTo',
|
||||||
'angledLineThatIntersects',
|
'angledLineThatIntersects',
|
||||||
'tangentialArcTo',
|
'tangentialArcTo',
|
||||||
] as any as ToolTip[]
|
]
|
||||||
|
|
||||||
export async function executeAst({
|
export async function executeAst({
|
||||||
ast,
|
ast,
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
import { findUsesOfTagInPipe, getNodePathFromSourceRange } from './queryAst'
|
import { findUsesOfTagInPipe, getNodePathFromSourceRange } from './queryAst'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
import { SimplifiedArgDetails } from './std/stdTypes'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
@ -627,7 +628,7 @@ describe('Testing removeSingleConstraintInfo', () => {
|
|||||||
'offset',
|
'offset',
|
||||||
],
|
],
|
||||||
['tangentialArcTo([3.14 + 0, 13.14], %)', 'arrayIndex', 1],
|
['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)
|
const ast = parse(code)
|
||||||
if (err(ast)) throw ast
|
if (err(ast)) throw ast
|
||||||
|
|
||||||
@ -638,11 +639,27 @@ describe('Testing removeSingleConstraintInfo', () => {
|
|||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
]
|
]
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
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(
|
const mod = removeSingleConstraintInfo(
|
||||||
{
|
pathToNode,
|
||||||
pathToCallExp: pathToNode,
|
argPosition,
|
||||||
[key]: value,
|
|
||||||
},
|
|
||||||
ast,
|
ast,
|
||||||
programMemory
|
programMemory
|
||||||
)
|
)
|
||||||
@ -675,12 +692,24 @@ describe('Testing removeSingleConstraintInfo', () => {
|
|||||||
code.indexOf(lineOfInterest) + 1,
|
code.indexOf(lineOfInterest) + 1,
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
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 pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
const mod = removeSingleConstraintInfo(
|
const mod = removeSingleConstraintInfo(
|
||||||
{
|
pathToNode,
|
||||||
pathToCallExp: pathToNode,
|
argPosition,
|
||||||
[key]: value,
|
|
||||||
},
|
|
||||||
ast,
|
ast,
|
||||||
programMemory
|
programMemory
|
||||||
)
|
)
|
||||||
|
@ -38,7 +38,7 @@ import {
|
|||||||
import { DefaultPlaneStr } from 'clientSideScene/sceneEntities'
|
import { DefaultPlaneStr } from 'clientSideScene/sceneEntities'
|
||||||
import { isOverlap, roundOff } from 'lib/utils'
|
import { isOverlap, roundOff } from 'lib/utils'
|
||||||
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
|
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 { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
|
|
||||||
@ -799,15 +799,10 @@ export function deleteSegmentFromPipeExpression(
|
|||||||
)
|
)
|
||||||
if (!constraintInfo) return
|
if (!constraintInfo) return
|
||||||
|
|
||||||
const input = makeRemoveSingleConstraintInput(
|
if (!constraintInfo.argPosition) return
|
||||||
constraintInfo.argPosition,
|
|
||||||
callExp.shallowPath
|
|
||||||
)
|
|
||||||
if (!input) return
|
|
||||||
const transform = removeSingleConstraintInfo(
|
const transform = removeSingleConstraintInfo(
|
||||||
{
|
callExp.shallowPath,
|
||||||
...input,
|
constraintInfo.argPosition,
|
||||||
},
|
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
programMemory
|
programMemory
|
||||||
)
|
)
|
||||||
@ -834,37 +829,9 @@ export function deleteSegmentFromPipeExpression(
|
|||||||
return _modifiedAst
|
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(
|
export function removeSingleConstraintInfo(
|
||||||
{
|
pathToCallExp: PathToNode,
|
||||||
pathToCallExp,
|
argDetails: SimplifiedArgDetails,
|
||||||
arrayIndex,
|
|
||||||
objectProperty,
|
|
||||||
}: {
|
|
||||||
pathToCallExp: PathToNode
|
|
||||||
arrayIndex?: number
|
|
||||||
objectProperty?: string
|
|
||||||
},
|
|
||||||
ast: Program,
|
ast: Program,
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
):
|
):
|
||||||
@ -875,8 +842,7 @@ export function removeSingleConstraintInfo(
|
|||||||
| false {
|
| false {
|
||||||
const transform = removeSingleConstraint({
|
const transform = removeSingleConstraint({
|
||||||
pathToCallExp,
|
pathToCallExp,
|
||||||
arrayIndex,
|
inputDetails: argDetails,
|
||||||
objectProperty,
|
|
||||||
ast,
|
ast,
|
||||||
})
|
})
|
||||||
if (!transform) return false
|
if (!transform) return false
|
||||||
|
@ -16,6 +16,7 @@ import {
|
|||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
sketchGroupFromKclValue,
|
sketchGroupFromKclValue,
|
||||||
|
ObjectExpression,
|
||||||
} from './wasm'
|
} from './wasm'
|
||||||
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
|
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
|
||||||
import { getSketchSegmentFromSourceRange } from './std/sketchConstraints'
|
import { getSketchSegmentFromSourceRange } from './std/sketchConstraints'
|
||||||
@ -934,3 +935,12 @@ export function hasExtrudableGeometry(ast: Program) {
|
|||||||
})
|
})
|
||||||
return Object.keys(theMap).length > 0
|
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,
|
ast,
|
||||||
programMemory,
|
programMemory,
|
||||||
[sourceStart, sourceStart + lineToChange.length],
|
[sourceStart, sourceStart + lineToChange.length],
|
||||||
[2, 3],
|
{
|
||||||
[0, 0]
|
type: 'straight-segment',
|
||||||
|
from: [0, 0],
|
||||||
|
to: [2, 3],
|
||||||
|
}
|
||||||
)
|
)
|
||||||
if (err(changeSketchArgsRetVal)) return changeSketchArgsRetVal
|
if (err(changeSketchArgsRetVal)) return changeSketchArgsRetVal
|
||||||
expect(recast(changeSketchArgsRetVal.modifiedAst)).toBe(expectedCode)
|
expect(recast(changeSketchArgsRetVal.modifiedAst)).toBe(expectedCode)
|
||||||
@ -150,8 +153,11 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
const newSketchLnRetVal = addNewSketchLn({
|
const newSketchLnRetVal = addNewSketchLn({
|
||||||
node: ast,
|
node: ast,
|
||||||
programMemory,
|
programMemory,
|
||||||
to: [2, 3],
|
input: {
|
||||||
from: [0, 0],
|
type: 'straight-segment',
|
||||||
|
from: [0, 0],
|
||||||
|
to: [2, 3],
|
||||||
|
},
|
||||||
fnName: 'lineTo',
|
fnName: 'lineTo',
|
||||||
pathToNode: [
|
pathToNode: [
|
||||||
['body', ''],
|
['body', ''],
|
||||||
|
@ -9,7 +9,6 @@ import {
|
|||||||
CallExpression,
|
CallExpression,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
Expr,
|
Expr,
|
||||||
Literal,
|
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
Identifier,
|
Identifier,
|
||||||
sketchGroupFromKclValue,
|
sketchGroupFromKclValue,
|
||||||
@ -20,7 +19,6 @@ import {
|
|||||||
getNodePathFromSourceRange,
|
getNodePathFromSourceRange,
|
||||||
} from 'lang/queryAst'
|
} from 'lang/queryAst'
|
||||||
import {
|
import {
|
||||||
LineInputsType,
|
|
||||||
isLiteralArrayOrStatic,
|
isLiteralArrayOrStatic,
|
||||||
isNotLiteralArrayOrStatic,
|
isNotLiteralArrayOrStatic,
|
||||||
} from 'lang/std/sketchcombos'
|
} from 'lang/std/sketchcombos'
|
||||||
@ -29,15 +27,15 @@ import { createPipeExpression, splitPathAtPipeExpression } from '../modifyAst'
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
SketchLineHelper,
|
SketchLineHelper,
|
||||||
TransformCallback,
|
|
||||||
ConstrainInfo,
|
ConstrainInfo,
|
||||||
RawValues,
|
|
||||||
ArrayItemInput,
|
ArrayItemInput,
|
||||||
ObjectPropertyInput,
|
ObjectPropertyInput,
|
||||||
SingleValueInput,
|
SingleValueInput,
|
||||||
VarValueKeys,
|
|
||||||
ArrayOrObjItemInput,
|
|
||||||
AddTagInfo,
|
AddTagInfo,
|
||||||
|
SegmentInputs,
|
||||||
|
SimplifiedArgDetails,
|
||||||
|
RawArgs,
|
||||||
|
CreatedSketchExprResult,
|
||||||
} from 'lang/std/stdTypes'
|
} from 'lang/std/stdTypes'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -56,6 +54,10 @@ import { err } from 'lib/trap'
|
|||||||
import { perpendicularDistance } from 'sketch-helpers'
|
import { perpendicularDistance } from 'sketch-helpers'
|
||||||
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
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 type Coords2d = [number, number]
|
||||||
|
|
||||||
export function getCoordsFromPaths(skGroup: SketchGroup, index = 0): Coords2d {
|
export function getCoordsFromPaths(skGroup: SketchGroup, index = 0): Coords2d {
|
||||||
@ -109,6 +111,7 @@ type AbbreviatedInput =
|
|||||||
| ArrayItemInput<any>['index']
|
| ArrayItemInput<any>['index']
|
||||||
| ObjectPropertyInput<any>['key']
|
| ObjectPropertyInput<any>['key']
|
||||||
| SingleValueInput<any>['type']
|
| SingleValueInput<any>['type']
|
||||||
|
| SimplifiedArgDetails
|
||||||
| undefined
|
| undefined
|
||||||
|
|
||||||
const constrainInfo = (
|
const constrainInfo = (
|
||||||
@ -157,8 +160,12 @@ const commonConstraintInfoHelper = (
|
|||||||
const firstArg = callExp.arguments?.[0]
|
const firstArg = callExp.arguments?.[0]
|
||||||
const isArr = firstArg.type === 'ArrayExpression'
|
const isArr = firstArg.type === 'ArrayExpression'
|
||||||
if (!isArr && firstArg.type !== 'ObjectExpression') return []
|
if (!isArr && firstArg.type !== 'ObjectExpression') return []
|
||||||
|
const pipeExpressionIndex = pathToNode.findIndex(
|
||||||
|
([_, nodeName]) => nodeName === 'PipeExpression'
|
||||||
|
)
|
||||||
|
const pathToBase = pathToNode.slice(0, pipeExpressionIndex + 2)
|
||||||
const pathToArrayExpression: PathToNode = [
|
const pathToArrayExpression: PathToNode = [
|
||||||
...pathToNode,
|
...pathToBase,
|
||||||
['arguments', 'CallExpression'],
|
['arguments', 'CallExpression'],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
isArr
|
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'] {
|
function getTag(index = 2): SketchLineHelper['getTag'] {
|
||||||
return (callExp: CallExpression) => {
|
return (callExp: CallExpression) => {
|
||||||
if (callExp.type !== 'CallExpression')
|
if (callExp.type !== 'CallExpression')
|
||||||
@ -322,14 +290,9 @@ function getTag(index = 2): SketchLineHelper['getTag'] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const lineTo: SketchLineHelper = {
|
export const lineTo: SketchLineHelper = {
|
||||||
add: ({
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||||
node,
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
pathToNode,
|
const to = segmentInput.to
|
||||||
to,
|
|
||||||
createCallback,
|
|
||||||
replaceExisting,
|
|
||||||
referencedSegment,
|
|
||||||
}) => {
|
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||||
_node,
|
_node,
|
||||||
@ -349,15 +312,23 @@ export const lineTo: SketchLineHelper = {
|
|||||||
createPipeSubstitution(),
|
createPipeSubstitution(),
|
||||||
])
|
])
|
||||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
if (replaceExisting && createCallback) {
|
if (replaceExistingCallback) {
|
||||||
const { callExp, valueUsedInTransform } = createCallback(
|
const result = replaceExistingCallback([
|
||||||
newVals,
|
{
|
||||||
arrayRawValuesHelper([
|
type: 'arrayItem',
|
||||||
[createLiteral(roundOff(to[0], 2)), 'xAbsolute'],
|
index: 0,
|
||||||
[createLiteral(roundOff(to[1], 2)), 'yAbsolute'],
|
argType: 'xAbsolute',
|
||||||
]),
|
expr: createLiteral(roundOff(to[0], 2)),
|
||||||
referencedSegment
|
},
|
||||||
)
|
{
|
||||||
|
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
|
pipe.body[callIndex] = callExp
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
@ -372,7 +343,9 @@ export const lineTo: SketchLineHelper = {
|
|||||||
pathToNode,
|
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 _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
@ -407,13 +380,12 @@ export const line: SketchLineHelper = {
|
|||||||
node,
|
node,
|
||||||
previousProgramMemory,
|
previousProgramMemory,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
to,
|
segmentInput,
|
||||||
from,
|
replaceExistingCallback,
|
||||||
replaceExisting,
|
|
||||||
referencedSegment,
|
|
||||||
createCallback,
|
|
||||||
spliceBetween,
|
spliceBetween,
|
||||||
}) => {
|
}) => {
|
||||||
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
|
const { from, to } = segmentInput
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<PipeExpression | CallExpression>(
|
const nodeMeta = getNodeFromPath<PipeExpression | CallExpression>(
|
||||||
_node,
|
_node,
|
||||||
@ -433,7 +405,11 @@ export const line: SketchLineHelper = {
|
|||||||
const newXVal = createLiteral(roundOff(to[0] - from[0], 2))
|
const newXVal = createLiteral(roundOff(to[0] - from[0], 2))
|
||||||
const newYVal = createLiteral(roundOff(to[1] - from[1], 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', [
|
const callExp = createCallExpression('line', [
|
||||||
createArrayExpression([newXVal, newYVal]),
|
createArrayExpression([newXVal, newYVal]),
|
||||||
createPipeSubstitution(),
|
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 { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
const { callExp, valueUsedInTransform } = createCallback(
|
const result = replaceExistingCallback([
|
||||||
[newXVal, newYVal],
|
{
|
||||||
arrayRawValuesHelper([
|
type: 'arrayItem',
|
||||||
[createLiteral(roundOff(to[0] - from[0], 2)), 'xRelative'],
|
index: 0,
|
||||||
[createLiteral(roundOff(to[1] - from[1], 2)), 'yRelative'],
|
argType: 'xRelative',
|
||||||
]),
|
expr: createLiteral(roundOff(to[0] - from[0], 2)),
|
||||||
referencedSegment
|
},
|
||||||
)
|
{
|
||||||
|
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
|
pipe.body[callIndex] = callExp
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
@ -496,7 +480,9 @@ export const line: SketchLineHelper = {
|
|||||||
pathToNode,
|
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 _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
@ -530,7 +516,9 @@ export const line: SketchLineHelper = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const xLineTo: 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 _node = { ...node }
|
||||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||||
@ -539,12 +527,17 @@ export const xLineTo: SketchLineHelper = {
|
|||||||
|
|
||||||
const newVal = createLiteral(roundOff(to[0], 2))
|
const newVal = createLiteral(roundOff(to[0], 2))
|
||||||
|
|
||||||
if (replaceExisting && createCallback) {
|
if (replaceExistingCallback) {
|
||||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
const { callExp, valueUsedInTransform } = createCallback(
|
const result = replaceExistingCallback([
|
||||||
[newVal, newVal],
|
{
|
||||||
singleRawValueHelper(newVal, 'xAbsolute')
|
type: 'singleValue',
|
||||||
)
|
argType: 'xAbsolute',
|
||||||
|
expr: createLiteral(roundOff(to[0], 2)),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
if (err(result)) return result
|
||||||
|
const { callExp, valueUsedInTransform } = result
|
||||||
pipe.body[callIndex] = callExp
|
pipe.body[callIndex] = callExp
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
@ -562,7 +555,9 @@ export const xLineTo: SketchLineHelper = {
|
|||||||
pathToNode,
|
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 _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
@ -591,7 +586,9 @@ export const xLineTo: SketchLineHelper = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const yLineTo: 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 _node = { ...node }
|
||||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||||
@ -600,12 +597,17 @@ export const yLineTo: SketchLineHelper = {
|
|||||||
|
|
||||||
const newVal = createLiteral(roundOff(to[1], 2))
|
const newVal = createLiteral(roundOff(to[1], 2))
|
||||||
|
|
||||||
if (replaceExisting && createCallback) {
|
if (replaceExistingCallback) {
|
||||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
const { callExp, valueUsedInTransform } = createCallback(
|
const result = replaceExistingCallback([
|
||||||
[newVal, newVal],
|
{
|
||||||
singleRawValueHelper(newVal, 'yAbsolute')
|
type: 'singleValue',
|
||||||
)
|
argType: 'yAbsolute',
|
||||||
|
expr: newVal,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
if (err(result)) return result
|
||||||
|
const { callExp, valueUsedInTransform } = result
|
||||||
pipe.body[callIndex] = callExp
|
pipe.body[callIndex] = callExp
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
@ -623,7 +625,9 @@ export const yLineTo: SketchLineHelper = {
|
|||||||
pathToNode,
|
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 _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
@ -652,7 +656,9 @@ export const yLineTo: SketchLineHelper = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const xLine: 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 _node = { ...node }
|
||||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||||
@ -660,14 +666,18 @@ export const xLine: SketchLineHelper = {
|
|||||||
const { node: pipe } = _node1
|
const { node: pipe } = _node1
|
||||||
|
|
||||||
const newVal = createLiteral(roundOff(to[0] - from[0], 2))
|
const newVal = createLiteral(roundOff(to[0] - from[0], 2))
|
||||||
const firstArg = newVal
|
|
||||||
|
|
||||||
if (replaceExisting && createCallback) {
|
if (replaceExistingCallback) {
|
||||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
const { callExp, valueUsedInTransform } = createCallback(
|
const result = replaceExistingCallback([
|
||||||
[firstArg, firstArg],
|
{
|
||||||
singleRawValueHelper(firstArg, 'xRelative')
|
type: 'singleValue',
|
||||||
)
|
argType: 'xRelative',
|
||||||
|
expr: newVal,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
if (err(result)) return result
|
||||||
|
const { callExp, valueUsedInTransform } = result
|
||||||
pipe.body[callIndex] = callExp
|
pipe.body[callIndex] = callExp
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
@ -677,13 +687,15 @@ export const xLine: SketchLineHelper = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const newLine = createCallExpression('xLine', [
|
const newLine = createCallExpression('xLine', [
|
||||||
firstArg,
|
newVal,
|
||||||
createPipeSubstitution(),
|
createPipeSubstitution(),
|
||||||
])
|
])
|
||||||
pipe.body = [...pipe.body, newLine]
|
pipe.body = [...pipe.body, newLine]
|
||||||
return { modifiedAst: _node, pathToNode }
|
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 _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
@ -712,19 +724,26 @@ export const xLine: SketchLineHelper = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const yLine: 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 _node = { ...node }
|
||||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||||
if (err(_node1)) return _node1
|
if (err(_node1)) return _node1
|
||||||
const { node: pipe } = _node1
|
const { node: pipe } = _node1
|
||||||
const newVal = createLiteral(roundOff(to[1] - from[1], 2))
|
const newVal = createLiteral(roundOff(to[1] - from[1], 2))
|
||||||
if (replaceExisting && createCallback) {
|
if (replaceExistingCallback) {
|
||||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
const { callExp, valueUsedInTransform } = createCallback(
|
const result = replaceExistingCallback([
|
||||||
[newVal, newVal],
|
{
|
||||||
singleRawValueHelper(newVal, 'yRelative')
|
type: 'singleValue',
|
||||||
)
|
argType: 'yRelative',
|
||||||
|
expr: newVal,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
if (err(result)) return result
|
||||||
|
const { callExp, valueUsedInTransform } = result
|
||||||
pipe.body[callIndex] = callExp
|
pipe.body[callIndex] = callExp
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
@ -740,7 +759,9 @@ export const yLine: SketchLineHelper = {
|
|||||||
pipe.body = [...pipe.body, newLine]
|
pipe.body = [...pipe.body, newLine]
|
||||||
return { modifiedAst: _node, pathToNode }
|
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 _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
@ -769,14 +790,9 @@ export const yLine: SketchLineHelper = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const tangentialArcTo: SketchLineHelper = {
|
export const tangentialArcTo: SketchLineHelper = {
|
||||||
add: ({
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||||
node,
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
pathToNode,
|
const { to } = segmentInput
|
||||||
to,
|
|
||||||
createCallback,
|
|
||||||
replaceExisting,
|
|
||||||
referencedSegment,
|
|
||||||
}) => {
|
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||||
const _node1 = getNode<PipeExpression | CallExpression>('PipeExpression')
|
const _node1 = getNode<PipeExpression | CallExpression>('PipeExpression')
|
||||||
@ -793,16 +809,24 @@ export const tangentialArcTo: SketchLineHelper = {
|
|||||||
const toX = createLiteral(roundOff(to[0], 2))
|
const toX = createLiteral(roundOff(to[0], 2))
|
||||||
const toY = createLiteral(roundOff(to[1], 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 { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
const { callExp, valueUsedInTransform } = createCallback(
|
const result = replaceExistingCallback([
|
||||||
[toX, toY],
|
{
|
||||||
arrayRawValuesHelper([
|
type: 'arrayItem',
|
||||||
[createLiteral(roundOff(to[0], 2)), 'xAbsolute'],
|
index: 0,
|
||||||
[createLiteral(roundOff(to[1], 2)), 'yAbsolute'],
|
argType: 'xRelative',
|
||||||
]),
|
expr: toX,
|
||||||
referencedSegment
|
},
|
||||||
)
|
{
|
||||||
|
type: 'arrayItem',
|
||||||
|
index: 1,
|
||||||
|
argType: 'yAbsolute',
|
||||||
|
expr: toY,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
if (err(result)) return result
|
||||||
|
const { callExp, valueUsedInTransform } = result
|
||||||
pipe.body[callIndex] = callExp
|
pipe.body[callIndex] = callExp
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
@ -832,7 +856,9 @@ export const tangentialArcTo: SketchLineHelper = {
|
|||||||
pathToNode,
|
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 _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
@ -900,15 +926,9 @@ export const tangentialArcTo: SketchLineHelper = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
export const angledLine: SketchLineHelper = {
|
export const angledLine: SketchLineHelper = {
|
||||||
add: ({
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||||
node,
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
pathToNode,
|
const { from, to } = segmentInput
|
||||||
to,
|
|
||||||
from,
|
|
||||||
createCallback,
|
|
||||||
replaceExisting,
|
|
||||||
referencedSegment,
|
|
||||||
}) => {
|
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||||
@ -922,16 +942,26 @@ export const angledLine: SketchLineHelper = {
|
|||||||
createPipeSubstitution(),
|
createPipeSubstitution(),
|
||||||
])
|
])
|
||||||
|
|
||||||
if (replaceExisting && createCallback) {
|
if (replaceExistingCallback) {
|
||||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
const { callExp, valueUsedInTransform } = createCallback(
|
const result = replaceExistingCallback([
|
||||||
[newAngleVal, newLengthVal],
|
{
|
||||||
arrOrObjectRawValuesHelper([
|
type: 'arrayOrObjItem',
|
||||||
[newAngleVal, 'angle', 'angle'],
|
index: 0,
|
||||||
[newLengthVal, 'length', 'length'],
|
key: 'angle',
|
||||||
]),
|
argType: 'angle',
|
||||||
referencedSegment
|
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
|
pipe.body[callIndex] = callExp
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
@ -946,7 +976,9 @@ export const angledLine: SketchLineHelper = {
|
|||||||
pathToNode,
|
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 _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
@ -988,11 +1020,11 @@ export const angledLineOfXLength: SketchLineHelper = {
|
|||||||
node,
|
node,
|
||||||
previousProgramMemory,
|
previousProgramMemory,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
to,
|
segmentInput,
|
||||||
from,
|
replaceExistingCallback,
|
||||||
createCallback,
|
|
||||||
replaceExisting,
|
|
||||||
}) => {
|
}) => {
|
||||||
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
|
const { from, to } = segmentInput
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||||
_node,
|
_node,
|
||||||
@ -1019,20 +1051,34 @@ export const angledLineOfXLength: SketchLineHelper = {
|
|||||||
}
|
}
|
||||||
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
||||||
const xLength = createLiteral(roundOff(Math.abs(from[0] - to[0]), 2) || 0.1)
|
const xLength = createLiteral(roundOff(Math.abs(from[0] - to[0]), 2) || 0.1)
|
||||||
const newLine = createCallback
|
let newLine: Expr
|
||||||
? createCallback(
|
if (replaceExistingCallback) {
|
||||||
[angle, xLength],
|
const result = replaceExistingCallback([
|
||||||
arrOrObjectRawValuesHelper([
|
{
|
||||||
[angle, 'angle', 'angle'],
|
type: 'arrayOrObjItem',
|
||||||
[xLength, 'xRelative', 'length'],
|
index: 0,
|
||||||
])
|
key: 'angle',
|
||||||
).callExp
|
argType: 'angle',
|
||||||
: createCallExpression('angledLineOfXLength', [
|
expr: angle,
|
||||||
createArrayExpression([angle, xLength]),
|
},
|
||||||
createPipeSubstitution(),
|
{
|
||||||
])
|
type: 'arrayOrObjItem',
|
||||||
|
index: 1,
|
||||||
|
key: 'length',
|
||||||
|
argType: 'xRelative',
|
||||||
|
expr: xLength,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
if (err(result)) return result
|
||||||
|
newLine = result.callExp
|
||||||
|
} else {
|
||||||
|
newLine = createCallExpression('angledLineOfXLength', [
|
||||||
|
createArrayExpression([angle, xLength]),
|
||||||
|
createPipeSubstitution(),
|
||||||
|
])
|
||||||
|
}
|
||||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
if (replaceExisting) {
|
if (replaceExistingCallback) {
|
||||||
pipe.body[callIndex] = newLine
|
pipe.body[callIndex] = newLine
|
||||||
} else {
|
} else {
|
||||||
pipe.body = [...pipe.body, newLine]
|
pipe.body = [...pipe.body, newLine]
|
||||||
@ -1042,7 +1088,9 @@ export const angledLineOfXLength: SketchLineHelper = {
|
|||||||
pathToNode,
|
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 _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
@ -1088,11 +1136,11 @@ export const angledLineOfYLength: SketchLineHelper = {
|
|||||||
node,
|
node,
|
||||||
previousProgramMemory,
|
previousProgramMemory,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
to,
|
segmentInput,
|
||||||
from,
|
replaceExistingCallback,
|
||||||
createCallback,
|
|
||||||
replaceExisting,
|
|
||||||
}) => {
|
}) => {
|
||||||
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
|
const { from, to } = segmentInput
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||||
_node,
|
_node,
|
||||||
@ -1117,20 +1165,34 @@ export const angledLineOfYLength: SketchLineHelper = {
|
|||||||
|
|
||||||
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
||||||
const yLength = createLiteral(roundOff(Math.abs(from[1] - to[1]), 2) || 0.1)
|
const yLength = createLiteral(roundOff(Math.abs(from[1] - to[1]), 2) || 0.1)
|
||||||
const newLine = createCallback
|
let newLine: Expr
|
||||||
? createCallback(
|
if (replaceExistingCallback) {
|
||||||
[angle, yLength],
|
const result = replaceExistingCallback([
|
||||||
arrOrObjectRawValuesHelper([
|
{
|
||||||
[angle, 'angle', 'angle'],
|
type: 'arrayOrObjItem',
|
||||||
[yLength, 'yRelative', 'length'],
|
index: 0,
|
||||||
])
|
key: 'angle',
|
||||||
).callExp
|
argType: 'angle',
|
||||||
: createCallExpression('angledLineOfYLength', [
|
expr: angle,
|
||||||
createArrayExpression([angle, yLength]),
|
},
|
||||||
createPipeSubstitution(),
|
{
|
||||||
])
|
type: 'arrayOrObjItem',
|
||||||
|
index: 1,
|
||||||
|
key: 'length',
|
||||||
|
argType: 'yRelative',
|
||||||
|
expr: yLength,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
if (err(result)) return result
|
||||||
|
newLine = result.callExp
|
||||||
|
} else {
|
||||||
|
newLine = createCallExpression('angledLineOfYLength', [
|
||||||
|
createArrayExpression([angle, yLength]),
|
||||||
|
createPipeSubstitution(),
|
||||||
|
])
|
||||||
|
}
|
||||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
if (replaceExisting) {
|
if (replaceExistingCallback) {
|
||||||
pipe.body[callIndex] = newLine
|
pipe.body[callIndex] = newLine
|
||||||
} else {
|
} else {
|
||||||
pipe.body = [...pipe.body, newLine]
|
pipe.body = [...pipe.body, newLine]
|
||||||
@ -1140,7 +1202,9 @@ export const angledLineOfYLength: SketchLineHelper = {
|
|||||||
pathToNode,
|
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 _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
@ -1182,15 +1246,9 @@ export const angledLineOfYLength: SketchLineHelper = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const angledLineToX: SketchLineHelper = {
|
export const angledLineToX: SketchLineHelper = {
|
||||||
add: ({
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||||
node,
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
pathToNode,
|
const { from, to } = segmentInput
|
||||||
to,
|
|
||||||
from,
|
|
||||||
createCallback,
|
|
||||||
replaceExisting,
|
|
||||||
referencedSegment,
|
|
||||||
}) => {
|
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||||
_node,
|
_node,
|
||||||
@ -1202,15 +1260,25 @@ export const angledLineToX: SketchLineHelper = {
|
|||||||
const { node: pipe } = nodeMeta
|
const { node: pipe } = nodeMeta
|
||||||
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
||||||
const xArg = createLiteral(roundOff(to[0], 2))
|
const xArg = createLiteral(roundOff(to[0], 2))
|
||||||
if (replaceExisting && createCallback) {
|
if (replaceExistingCallback) {
|
||||||
const { callExp, valueUsedInTransform } = createCallback(
|
const result = replaceExistingCallback([
|
||||||
[angle, xArg],
|
{
|
||||||
arrOrObjectRawValuesHelper([
|
type: 'arrayOrObjItem',
|
||||||
[angle, 'angle', 'angle'],
|
index: 0,
|
||||||
[xArg, 'xAbsolute', 'to'],
|
key: 'angle',
|
||||||
]),
|
argType: 'angle',
|
||||||
referencedSegment
|
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)
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
pipe.body[callIndex] = callExp
|
pipe.body[callIndex] = callExp
|
||||||
return {
|
return {
|
||||||
@ -1230,7 +1298,9 @@ export const angledLineToX: SketchLineHelper = {
|
|||||||
pathToNode,
|
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 _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
@ -1270,15 +1340,9 @@ export const angledLineToX: SketchLineHelper = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const angledLineToY: SketchLineHelper = {
|
export const angledLineToY: SketchLineHelper = {
|
||||||
add: ({
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||||
node,
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
pathToNode,
|
const { from, to } = segmentInput
|
||||||
to,
|
|
||||||
from,
|
|
||||||
createCallback,
|
|
||||||
replaceExisting,
|
|
||||||
referencedSegment,
|
|
||||||
}) => {
|
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||||
_node,
|
_node,
|
||||||
@ -1292,15 +1356,25 @@ export const angledLineToY: SketchLineHelper = {
|
|||||||
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
||||||
const yArg = createLiteral(roundOff(to[1], 2))
|
const yArg = createLiteral(roundOff(to[1], 2))
|
||||||
|
|
||||||
if (replaceExisting && createCallback) {
|
if (replaceExistingCallback) {
|
||||||
const { callExp, valueUsedInTransform } = createCallback(
|
const result = replaceExistingCallback([
|
||||||
[angle, yArg],
|
{
|
||||||
arrOrObjectRawValuesHelper([
|
type: 'arrayOrObjItem',
|
||||||
[angle, 'angle', 'angle'],
|
index: 0,
|
||||||
[yArg, 'yAbsolute', 'to'],
|
key: 'angle',
|
||||||
]),
|
argType: 'angle',
|
||||||
referencedSegment
|
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)
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
pipe.body[callIndex] = callExp
|
pipe.body[callIndex] = callExp
|
||||||
return {
|
return {
|
||||||
@ -1320,7 +1394,9 @@ export const angledLineToY: SketchLineHelper = {
|
|||||||
pathToNode,
|
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 _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
@ -1363,12 +1439,12 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
|||||||
add: ({
|
add: ({
|
||||||
node,
|
node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
to,
|
segmentInput,
|
||||||
from,
|
replaceExistingCallback,
|
||||||
createCallback,
|
|
||||||
replaceExisting,
|
|
||||||
referencedSegment,
|
referencedSegment,
|
||||||
}) => {
|
}) => {
|
||||||
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
|
const { from, to } = segmentInput
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||||
_node,
|
_node,
|
||||||
@ -1395,24 +1471,23 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (replaceExisting && createCallback) {
|
if (replaceExistingCallback) {
|
||||||
const { callExp, valueUsedInTransform } = createCallback(
|
const result = replaceExistingCallback([
|
||||||
[angle, offset],
|
{
|
||||||
[
|
type: 'objectProperty',
|
||||||
{
|
key: 'angle',
|
||||||
type: 'objectProperty',
|
argType: 'angle',
|
||||||
key: 'angle',
|
expr: angle,
|
||||||
value: angle,
|
},
|
||||||
argType: 'angle',
|
{
|
||||||
},
|
type: 'objectProperty',
|
||||||
{
|
key: 'offset',
|
||||||
type: 'objectProperty',
|
argType: 'intersectionOffset',
|
||||||
key: 'offset',
|
expr: offset,
|
||||||
value: offset,
|
},
|
||||||
argType: 'intersectionOffset',
|
])
|
||||||
},
|
if (err(result)) return result
|
||||||
]
|
const { callExp, valueUsedInTransform } = result
|
||||||
)
|
|
||||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
pipe.body[callIndex] = callExp
|
pipe.body[callIndex] = callExp
|
||||||
return {
|
return {
|
||||||
@ -1423,7 +1498,9 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
|||||||
}
|
}
|
||||||
return new Error('not implemented')
|
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 _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
@ -1559,8 +1636,10 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
|||||||
export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
|
export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
|
||||||
node,
|
node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
to,
|
input,
|
||||||
}) => {
|
}) => {
|
||||||
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
|
const { to } = input
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) {
|
if (err(nodeMeta)) {
|
||||||
@ -1615,8 +1694,7 @@ export function changeSketchArguments(
|
|||||||
node: Program,
|
node: Program,
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
args: [number, number],
|
input: SegmentInputs
|
||||||
from: [number, number]
|
|
||||||
): { modifiedAst: Program; pathToNode: PathToNode } | Error {
|
): { modifiedAst: Program; pathToNode: PathToNode } | Error {
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const thePath = getNodePathFromSourceRange(_node, sourceRange)
|
const thePath = getNodePathFromSourceRange(_node, sourceRange)
|
||||||
@ -1635,8 +1713,7 @@ export function changeSketchArguments(
|
|||||||
node: _node,
|
node: _node,
|
||||||
previousProgramMemory: programMemory,
|
previousProgramMemory: programMemory,
|
||||||
pathToNode: shallowPath,
|
pathToNode: shallowPath,
|
||||||
to: args,
|
input,
|
||||||
from,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1684,8 +1761,7 @@ export function compareVec2Epsilon2(
|
|||||||
interface CreateLineFnCallArgs {
|
interface CreateLineFnCallArgs {
|
||||||
node: Program
|
node: Program
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
to: [number, number]
|
input: SegmentInputs
|
||||||
from: [number, number]
|
|
||||||
fnName: ToolTip
|
fnName: ToolTip
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
spliceBetween?: boolean
|
spliceBetween?: boolean
|
||||||
@ -1694,10 +1770,9 @@ interface CreateLineFnCallArgs {
|
|||||||
export function addNewSketchLn({
|
export function addNewSketchLn({
|
||||||
node: _node,
|
node: _node,
|
||||||
programMemory: previousProgramMemory,
|
programMemory: previousProgramMemory,
|
||||||
to,
|
|
||||||
fnName,
|
fnName,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
from,
|
input: segmentInput,
|
||||||
spliceBetween = false,
|
spliceBetween = false,
|
||||||
}: CreateLineFnCallArgs):
|
}: CreateLineFnCallArgs):
|
||||||
| {
|
| {
|
||||||
@ -1721,9 +1796,7 @@ export function addNewSketchLn({
|
|||||||
node,
|
node,
|
||||||
previousProgramMemory,
|
previousProgramMemory,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
to,
|
segmentInput,
|
||||||
from,
|
|
||||||
replaceExisting: false,
|
|
||||||
spliceBetween,
|
spliceBetween,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1784,18 +1857,16 @@ export function replaceSketchLine({
|
|||||||
programMemory,
|
programMemory,
|
||||||
pathToNode: _pathToNode,
|
pathToNode: _pathToNode,
|
||||||
fnName,
|
fnName,
|
||||||
to,
|
segmentInput,
|
||||||
from,
|
replaceExistingCallback,
|
||||||
createCallback,
|
|
||||||
referencedSegment,
|
referencedSegment,
|
||||||
}: {
|
}: {
|
||||||
node: Program
|
node: Program
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
fnName: ToolTip
|
fnName: ToolTip
|
||||||
to: [number, number]
|
segmentInput: SegmentInputs
|
||||||
from: [number, number]
|
replaceExistingCallback: (rawArgs: RawArgs) => CreatedSketchExprResult | Error
|
||||||
createCallback: TransformCallback
|
|
||||||
referencedSegment?: Path
|
referencedSegment?: Path
|
||||||
}):
|
}):
|
||||||
| {
|
| {
|
||||||
@ -1805,7 +1876,7 @@ export function replaceSketchLine({
|
|||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
if (![...toolTips, 'intersect'].includes(fnName)) {
|
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 }
|
const _node = { ...node }
|
||||||
|
|
||||||
@ -1815,10 +1886,8 @@ export function replaceSketchLine({
|
|||||||
previousProgramMemory: programMemory,
|
previousProgramMemory: programMemory,
|
||||||
pathToNode: _pathToNode,
|
pathToNode: _pathToNode,
|
||||||
referencedSegment,
|
referencedSegment,
|
||||||
to,
|
segmentInput,
|
||||||
from,
|
replaceExistingCallback,
|
||||||
replaceExisting: true,
|
|
||||||
createCallback,
|
|
||||||
})
|
})
|
||||||
if (err(addRetVal)) return addRetVal
|
if (err(addRetVal)) return addRetVal
|
||||||
|
|
||||||
@ -1826,13 +1895,16 @@ export function replaceSketchLine({
|
|||||||
return { modifiedAst, valueUsedInTransform, pathToNode }
|
return { modifiedAst, valueUsedInTransform, pathToNode }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addTagForSketchOnFace(a: AddTagInfo, expressionName: string) {
|
export function addTagForSketchOnFace(
|
||||||
|
tagInfo: AddTagInfo,
|
||||||
|
expressionName: string
|
||||||
|
) {
|
||||||
if (expressionName === 'close') {
|
if (expressionName === 'close') {
|
||||||
return addTag(1)(a)
|
return addTag(1)(tagInfo)
|
||||||
}
|
}
|
||||||
if (expressionName in sketchLineHelperMap) {
|
if (expressionName in sketchLineHelperMap) {
|
||||||
const { addTag } = sketchLineHelperMap[expressionName]
|
const { addTag } = sketchLineHelperMap[expressionName]
|
||||||
return addTag(a)
|
return addTag(tagInfo)
|
||||||
}
|
}
|
||||||
return new Error(`"${expressionName}" is not a sketch line helper`)
|
return new Error(`"${expressionName}" is not a sketch line helper`)
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,17 @@ export function getSketchSegmentFromSourceRange(
|
|||||||
index: number
|
index: number
|
||||||
}
|
}
|
||||||
| Error {
|
| 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
|
const startSourceRange = sketchGroup.start?.__geoMeta.sourceRange
|
||||||
if (
|
if (
|
||||||
startSourceRange &&
|
startSourceRange &&
|
||||||
@ -51,17 +62,7 @@ export function getSketchSegmentFromSourceRange(
|
|||||||
sketchGroup.start
|
sketchGroup.start
|
||||||
)
|
)
|
||||||
return { segment: { ...sketchGroup.start, type: 'Base' }, index: -1 }
|
return { segment: { ...sketchGroup.start, type: 'Base' }, index: -1 }
|
||||||
|
return new Error('could not find matching segment')
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isSketchVariablesLinked(
|
export function isSketchVariablesLinked(
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -8,23 +8,10 @@ import {
|
|||||||
PathToNode,
|
PathToNode,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
Literal,
|
Literal,
|
||||||
|
BinaryPart,
|
||||||
} from '../wasm'
|
} from '../wasm'
|
||||||
import { EngineCommandManager } from './engineConnection'
|
|
||||||
import { LineInputsType } from './sketchcombos'
|
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 {
|
export interface ModifyAstBase {
|
||||||
node: Program
|
node: Program
|
||||||
// TODO #896: Remove ProgramMemory from this interface
|
// TODO #896: Remove ProgramMemory from this interface
|
||||||
@ -37,76 +24,162 @@ export interface AddTagInfo {
|
|||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
}
|
}
|
||||||
|
|
||||||
interface addCall extends ModifyAstBase {
|
/** Inputs for all straight segments, to and from are absolute values, as this gives a
|
||||||
to: [number, number]
|
* 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]
|
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
|
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
|
spliceBetween?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface updateArgs extends ModifyAstBase {
|
interface updateArgs extends ModifyAstBase {
|
||||||
from: [number, number]
|
input: SegmentInputs
|
||||||
to: [number, number]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VarValueKeys = 'angle' | 'offset' | 'length' | 'to' | 'intersectTag'
|
export type InputArgKeys = 'angle' | 'offset' | 'length' | 'to' | 'intersectTag'
|
||||||
export interface SingleValueInput<T> {
|
export interface SingleValueInput<T> {
|
||||||
type: 'singleValue'
|
type: 'singleValue'
|
||||||
argType: LineInputsType
|
argType: LineInputsType
|
||||||
value: T
|
expr: T
|
||||||
}
|
}
|
||||||
export interface ArrayItemInput<T> {
|
export interface ArrayItemInput<T> {
|
||||||
type: 'arrayItem'
|
type: 'arrayItem'
|
||||||
index: 0 | 1
|
index: 0 | 1
|
||||||
argType: LineInputsType
|
argType: LineInputsType
|
||||||
value: T
|
expr: T
|
||||||
}
|
}
|
||||||
export interface ObjectPropertyInput<T> {
|
export interface ObjectPropertyInput<T> {
|
||||||
type: 'objectProperty'
|
type: 'objectProperty'
|
||||||
key: VarValueKeys
|
key: InputArgKeys
|
||||||
argType: LineInputsType
|
argType: LineInputsType
|
||||||
value: T
|
expr: T
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ArrayOrObjItemInput<T> {
|
interface ArrayOrObjItemInput<T> {
|
||||||
type: 'arrayOrObjItem'
|
type: 'arrayOrObjItem'
|
||||||
key: VarValueKeys
|
key: InputArgKeys
|
||||||
index: 0 | 1
|
index: 0 | 1
|
||||||
argType: LineInputsType
|
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>
|
| SingleValueInput<T>
|
||||||
| ArrayItemInput<T>
|
| ArrayItemInput<T>
|
||||||
| ObjectPropertyInput<T>
|
| ObjectPropertyInput<T>
|
||||||
| ArrayOrObjItemInput<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 =
|
export type InputArgs = Array<InputArg>
|
||||||
| {
|
|
||||||
type: 'singleValue'
|
|
||||||
}
|
|
||||||
| { type: 'arrayItem'; index: 0 | 1 }
|
|
||||||
| { type: 'objectProperty'; key: VarValueKeys }
|
|
||||||
|
|
||||||
export type TransformCallback = (
|
/**
|
||||||
args: [Expr, Expr],
|
* The literal equivalent of whatever current expression is
|
||||||
literalValues: RawValues,
|
* i.e. if the expression is 5 + 6, the literal would be 11
|
||||||
referencedSegment?: Path
|
* 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
|
callExp: Expr
|
||||||
valueUsedInTransform?: number
|
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 {
|
export interface ConstrainInfo {
|
||||||
stdLibFnName: ToolTip
|
stdLibFnName: ToolTip
|
||||||
type: LineInputsType | 'vertical' | 'horizontal' | 'tangentialWithPrevious'
|
type: LineInputsType | 'vertical' | 'horizontal' | 'tangentialWithPrevious'
|
||||||
@ -115,7 +188,7 @@ export interface ConstrainInfo {
|
|||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
value: string
|
value: string
|
||||||
calculatedValue?: any
|
calculatedValue?: any
|
||||||
argPosition?: SimplifiedVarValue
|
argPosition?: SimplifiedArgDetails
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SketchLineHelper {
|
export interface SketchLineHelper {
|
||||||
|
@ -20,10 +20,8 @@ import {
|
|||||||
} from 'lang/queryAst'
|
} from 'lang/queryAst'
|
||||||
import { CommandArgument } from './commandTypes'
|
import { CommandArgument } from './commandTypes'
|
||||||
import {
|
import {
|
||||||
STRAIGHT_SEGMENT,
|
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
|
||||||
getParentGroup,
|
getParentGroup,
|
||||||
PROFILE_START,
|
SEGMENT_BODIES_PLUS_PROFILE_START,
|
||||||
} from 'clientSideScene/sceneEntities'
|
} from 'clientSideScene/sceneEntities'
|
||||||
import { Mesh, Object3D, Object3DEventMap } from 'three'
|
import { Mesh, Object3D, Object3DEventMap } from 'three'
|
||||||
import { AXIS_GROUP, X_AXIS } from 'clientSideScene/sceneInfra'
|
import { AXIS_GROUP, X_AXIS } from 'clientSideScene/sceneInfra'
|
||||||
@ -162,11 +160,7 @@ export async function getEventForSelectWithPoint({
|
|||||||
export function getEventForSegmentSelection(
|
export function getEventForSegmentSelection(
|
||||||
obj: Object3D<Object3DEventMap>
|
obj: Object3D<Object3DEventMap>
|
||||||
): ModelingMachineEvent | null {
|
): ModelingMachineEvent | null {
|
||||||
const group = getParentGroup(obj, [
|
const group = getParentGroup(obj, SEGMENT_BODIES_PLUS_PROFILE_START)
|
||||||
STRAIGHT_SEGMENT,
|
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
|
||||||
PROFILE_START,
|
|
||||||
])
|
|
||||||
const axisGroup = getParentGroup(obj, [AXIS_GROUP])
|
const axisGroup = getParentGroup(obj, [AXIS_GROUP])
|
||||||
if (!group && !axisGroup) return null
|
if (!group && !axisGroup) return null
|
||||||
if (axisGroup?.userData.type === AXIS_GROUP) {
|
if (axisGroup?.userData.type === AXIS_GROUP) {
|
||||||
@ -303,12 +297,7 @@ function updateSceneObjectColors(codeBasedSelections: Selection[]) {
|
|||||||
const updated = kclManager.ast
|
const updated = kclManager.ast
|
||||||
|
|
||||||
Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
|
Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
|
||||||
if (
|
if (!SEGMENT_BODIES_PLUS_PROFILE_START.includes(segmentGroup?.name)) return
|
||||||
![STRAIGHT_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT, PROFILE_START].includes(
|
|
||||||
segmentGroup?.name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||||
updated,
|
updated,
|
||||||
segmentGroup.userData.pathToNode,
|
segmentGroup.userData.pathToNode,
|
||||||
|
Reference in New Issue
Block a user