Change perpendicular-distance constraint to something more intuitive to setting parallel distance (#110)
* some clean up * Change perpendicular-distance constraint to something more intuitive to setting parallel distance * delete * add back * force rename * change name back * try renaming again
This commit is contained in:
@ -7,10 +7,10 @@ import { EqualLength } from './components/Toolbar/EqualLength'
|
||||
import { EqualAngle } from './components/Toolbar/EqualAngle'
|
||||
import { Intersect } from './components/Toolbar/Intersect'
|
||||
import { SetHorzVertDistance } from './components/Toolbar/SetHorzVertDistance'
|
||||
import { SetAngleLength } from './components/Toolbar/SetAngleLength'
|
||||
import { SetAngleLength } from './components/Toolbar/setAngleLength'
|
||||
import { ConvertToVariable } from './components/Toolbar/ConvertVariable'
|
||||
import { SetAbsDistance } from './components/Toolbar/SetAbsDistance'
|
||||
import { SetAngleBetween } from './components/Toolbar/setAngleBetween'
|
||||
import { SetAngleBetween } from './components/Toolbar/SetAngleBetween'
|
||||
|
||||
export const Toolbar = () => {
|
||||
const {
|
||||
|
@ -79,6 +79,7 @@ function stringSplice(str: string, index: number, count: number, add: string) {
|
||||
return str.slice(0, index) + (add || '') + str.slice(index + count)
|
||||
}
|
||||
|
||||
// what a terriable name
|
||||
export function useCalc({
|
||||
value,
|
||||
initialVariableName: valueName = '',
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
import {
|
||||
getNodePathFromSourceRange,
|
||||
getNodeFromPath,
|
||||
isLinesParallelAndConstrained,
|
||||
} from '../../lang/queryAst'
|
||||
import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints'
|
||||
import {
|
||||
@ -17,10 +18,7 @@ import {
|
||||
getTransformInfos,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { GetInfoModal } from '../SetHorVertDistanceModal'
|
||||
import {
|
||||
createIdentifier,
|
||||
createVariableDeclaration,
|
||||
} from '../../lang/modifyAst'
|
||||
import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
|
||||
const getModalInfo = create(GetInfoModal as any)
|
||||
@ -37,9 +35,45 @@ export const Intersect = () => {
|
||||
)
|
||||
const [enable, setEnable] = useState(false)
|
||||
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
|
||||
const [forecdSelectionRanges, setForcedSelectionRanges] =
|
||||
useState<typeof selectionRanges>()
|
||||
useEffect(() => {
|
||||
if (!ast) return
|
||||
const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
|
||||
if (selectionRanges.codeBasedSelections.length < 2) {
|
||||
setEnable(false)
|
||||
setForcedSelectionRanges({ ...selectionRanges })
|
||||
return
|
||||
}
|
||||
|
||||
const previousSegment =
|
||||
selectionRanges.codeBasedSelections.length > 1 &&
|
||||
isLinesParallelAndConstrained(
|
||||
ast,
|
||||
programMemory,
|
||||
selectionRanges.codeBasedSelections[0],
|
||||
selectionRanges.codeBasedSelections[1]
|
||||
)
|
||||
const shouldUsePreviousSegment =
|
||||
selectionRanges.codeBasedSelections?.[1]?.type !== 'line-end' &&
|
||||
previousSegment &&
|
||||
previousSegment.isParallelAndConstrained
|
||||
console.log(shouldUsePreviousSegment)
|
||||
|
||||
const _forcedSelectionRanges: typeof selectionRanges = {
|
||||
...selectionRanges,
|
||||
codeBasedSelections: [
|
||||
selectionRanges.codeBasedSelections?.[0],
|
||||
shouldUsePreviousSegment
|
||||
? {
|
||||
range: previousSegment.sourceRange,
|
||||
type: 'line-end',
|
||||
}
|
||||
: selectionRanges.codeBasedSelections?.[1],
|
||||
],
|
||||
}
|
||||
setForcedSelectionRanges(_forcedSelectionRanges)
|
||||
|
||||
const paths = _forcedSelectionRanges.codeBasedSelections.map(({ range }) =>
|
||||
getNodePathFromSourceRange(ast, range)
|
||||
)
|
||||
const nodes = paths.map(
|
||||
@ -70,7 +104,8 @@ export const Intersect = () => {
|
||||
const theTransforms = getTransformInfos(
|
||||
{
|
||||
...selectionRanges,
|
||||
codeBasedSelections: selectionRanges.codeBasedSelections.slice(1),
|
||||
codeBasedSelections:
|
||||
_forcedSelectionRanges.codeBasedSelections.slice(1),
|
||||
},
|
||||
ast,
|
||||
'intersect'
|
||||
@ -81,7 +116,8 @@ export const Intersect = () => {
|
||||
secondaryVarDecs.length === 1 &&
|
||||
isAllTooltips &&
|
||||
isOthersLinkedToPrimary &&
|
||||
theTransforms.every(Boolean)
|
||||
theTransforms.every(Boolean) &&
|
||||
_forcedSelectionRanges?.codeBasedSelections?.[1]?.type === 'line-end'
|
||||
setEnable(_enableEqual)
|
||||
}, [guiMode, selectionRanges])
|
||||
if (guiMode.mode !== 'sketch') return null
|
||||
@ -89,11 +125,11 @@ export const Intersect = () => {
|
||||
return (
|
||||
<button
|
||||
onClick={async () => {
|
||||
if (transformInfos && ast) {
|
||||
if (transformInfos && ast && forecdSelectionRanges) {
|
||||
const { modifiedAst, tagInfo, valueUsedInTransform } =
|
||||
transformSecondarySketchLinesTagFirst({
|
||||
ast: JSON.parse(JSON.stringify(ast)),
|
||||
selectionRanges,
|
||||
selectionRanges: forecdSelectionRanges,
|
||||
transformInfos,
|
||||
programMemory,
|
||||
})
|
||||
@ -129,7 +165,7 @@ export const Intersect = () => {
|
||||
const { modifiedAst: _modifiedAst } =
|
||||
transformSecondarySketchLinesTagFirst({
|
||||
ast,
|
||||
selectionRanges,
|
||||
selectionRanges: forecdSelectionRanges,
|
||||
transformInfos,
|
||||
programMemory,
|
||||
forceSegName: segName,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { PathToNode, ProgramMemory } from './executor'
|
||||
import { Selection } from '../useStore'
|
||||
import { PathToNode, ProgramMemory, SketchGroup, SourceRange } from './executor'
|
||||
import { Selection, TooTip } from '../useStore'
|
||||
import {
|
||||
BinaryExpression,
|
||||
Program,
|
||||
@ -13,6 +13,13 @@ import {
|
||||
Identifier,
|
||||
} from './abstractSyntaxTree'
|
||||
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
|
||||
import { getSketchSegmentFromSourceRange } from './std/sketchConstraints'
|
||||
import { getAngle } from '../lib/utils'
|
||||
import { getFirstArg } from './std/sketch'
|
||||
import {
|
||||
getConstraintLevelFromSourceRange,
|
||||
getConstraintType,
|
||||
} from './std/sketchcombos'
|
||||
|
||||
export function getNodeFromPath<T>(
|
||||
node: Program,
|
||||
@ -242,7 +249,7 @@ export function getNodePathFromSourceRange(
|
||||
sourceRange: Selection['range'],
|
||||
previousPath: PathToNode = [['body', '']]
|
||||
): PathToNode {
|
||||
const [start, end] = sourceRange
|
||||
const [start, end] = sourceRange || []
|
||||
let path: PathToNode = [...previousPath]
|
||||
const _node = { ...node }
|
||||
|
||||
@ -409,3 +416,74 @@ export function isValueZero(val?: Value): boolean {
|
||||
Number(val.argument.value) === 0)
|
||||
)
|
||||
}
|
||||
|
||||
export function isLinesParallelAndConstrained(
|
||||
ast: Program,
|
||||
programMemory: ProgramMemory,
|
||||
primaryLine: Selection,
|
||||
secondaryLine: Selection
|
||||
): {
|
||||
isParallelAndConstrained: boolean
|
||||
sourceRange: SourceRange
|
||||
} {
|
||||
try {
|
||||
const EPSILON = 0.005
|
||||
const primaryPath = getNodePathFromSourceRange(ast, primaryLine.range)
|
||||
const secondaryPath = getNodePathFromSourceRange(ast, secondaryLine.range)
|
||||
const secondaryNode = getNodeFromPath<CallExpression>(
|
||||
ast,
|
||||
secondaryPath,
|
||||
'CallExpression'
|
||||
).node
|
||||
const varDec = getNodeFromPath(ast, primaryPath, 'VariableDeclaration').node
|
||||
const varName = (varDec as VariableDeclaration)?.declarations[0]?.id?.name
|
||||
const path = programMemory?.root[varName] as SketchGroup
|
||||
const primarySegment = getSketchSegmentFromSourceRange(
|
||||
path,
|
||||
primaryLine.range
|
||||
).segment
|
||||
const { segment: secondarySegment, index: secondaryIndex } =
|
||||
getSketchSegmentFromSourceRange(path, secondaryLine.range)
|
||||
const primaryAngle = getAngle(primarySegment.from, primarySegment.to)
|
||||
const secondaryAngle = getAngle(secondarySegment.from, secondarySegment.to)
|
||||
const secondaryAngleAlt = getAngle(
|
||||
secondarySegment.to,
|
||||
secondarySegment.from
|
||||
)
|
||||
const isParallel =
|
||||
Math.abs(primaryAngle - secondaryAngle) < EPSILON ||
|
||||
Math.abs(primaryAngle - secondaryAngleAlt) < EPSILON
|
||||
|
||||
// is secordary line fully constrain, or has constrain type of 'angle'
|
||||
const secondaryFirstArg = getFirstArg(secondaryNode)
|
||||
const constraintType = getConstraintType(
|
||||
secondaryFirstArg.val,
|
||||
secondaryNode.callee.name as TooTip
|
||||
)
|
||||
const constraintLevel = getConstraintLevelFromSourceRange(
|
||||
secondaryLine.range,
|
||||
ast
|
||||
)
|
||||
const isConstrained =
|
||||
constraintType === 'angle' || constraintLevel === 'full'
|
||||
|
||||
// get the previous segment
|
||||
const prevSegment = (programMemory.root[varName] as SketchGroup).value[
|
||||
secondaryIndex - 1
|
||||
]
|
||||
const prevSourceRange = prevSegment.__geoMeta.sourceRange
|
||||
|
||||
const isParallelAndConstrained =
|
||||
isParallel && isConstrained && !!prevSourceRange
|
||||
|
||||
return {
|
||||
isParallelAndConstrained,
|
||||
sourceRange: prevSourceRange,
|
||||
}
|
||||
} catch (e) {
|
||||
return {
|
||||
isParallelAndConstrained: false,
|
||||
sourceRange: [0, 0],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1351,7 +1351,7 @@ export function addNewSketchLn({
|
||||
const { from } = getSketchSegmentFromSourceRange(sketch, [
|
||||
defaultLine.start,
|
||||
defaultLine.end,
|
||||
])
|
||||
]).segment
|
||||
return updateArgs({
|
||||
node,
|
||||
previousProgramMemory,
|
||||
|
@ -386,7 +386,7 @@ show(part001)`
|
||||
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
|
||||
programMemory.root['part001'] as SketchGroup,
|
||||
[index, index]
|
||||
)
|
||||
).segment
|
||||
expect(segment).toEqual({
|
||||
type: 'toPoint',
|
||||
to: [5.62, 1.79],
|
||||
@ -399,7 +399,7 @@ show(part001)`
|
||||
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
|
||||
programMemory.root['part001'] as SketchGroup,
|
||||
[index, index]
|
||||
)
|
||||
).segment
|
||||
expect(segment).toEqual({ type: 'base', to: [0, 0.04], from: [0, 0.04] })
|
||||
})
|
||||
})
|
||||
|
@ -11,7 +11,10 @@ import { InternalFn } from './stdTypes'
|
||||
export function getSketchSegmentFromSourceRange(
|
||||
sketchGroup: SketchGroup,
|
||||
[rangeStart, rangeEnd]: SourceRange
|
||||
): SketchGroup['value'][number] {
|
||||
): {
|
||||
segment: SketchGroup['value'][number]
|
||||
index: number
|
||||
} {
|
||||
const startSourceRange = sketchGroup.start?.__geoMeta.sourceRange
|
||||
if (
|
||||
startSourceRange &&
|
||||
@ -19,14 +22,18 @@ export function getSketchSegmentFromSourceRange(
|
||||
startSourceRange[1] >= rangeEnd &&
|
||||
sketchGroup.start
|
||||
)
|
||||
return sketchGroup.start
|
||||
return { segment: sketchGroup.start, index: -1 }
|
||||
|
||||
const line = sketchGroup.value.find(
|
||||
const lineIndex = sketchGroup.value.findIndex(
|
||||
({ __geoMeta: { sourceRange } }) =>
|
||||
sourceRange[0] <= rangeStart && sourceRange[1] >= rangeEnd
|
||||
)
|
||||
const line = sketchGroup.value[lineIndex]
|
||||
if (!line) throw new Error('could not find matching line')
|
||||
return line
|
||||
return {
|
||||
segment: line,
|
||||
index: lineIndex,
|
||||
}
|
||||
}
|
||||
|
||||
export const segLen: InternalFn = (
|
||||
|
@ -1422,9 +1422,10 @@ export function transformAstSketchLines({
|
||||
const sketchGroup = programMemory.root?.[varName]
|
||||
if (!sketchGroup || sketchGroup.type !== 'sketchGroup')
|
||||
throw new Error('not a sketch group')
|
||||
const seg = getSketchSegmentFromSourceRange(sketchGroup, range)
|
||||
const seg = getSketchSegmentFromSourceRange(sketchGroup, range).segment
|
||||
const referencedSegment = referencedSegmentRange
|
||||
? getSketchSegmentFromSourceRange(sketchGroup, referencedSegmentRange)
|
||||
.segment
|
||||
: sketchGroup.value.find((path) => path.name === _referencedSegmentName)
|
||||
const { to, from } = seg
|
||||
const { modifiedAst, valueUsedInTransform } = replaceSketchLine({
|
||||
|
Reference in New Issue
Block a user