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:
Kurt Hutten
2023-04-08 14:16:49 +10:00
committed by GitHub
parent 0fa56c3f15
commit 2fc68e7c82
10 changed files with 146 additions and 23 deletions

View File

@ -7,10 +7,10 @@ import { EqualLength } from './components/Toolbar/EqualLength'
import { EqualAngle } from './components/Toolbar/EqualAngle' import { EqualAngle } from './components/Toolbar/EqualAngle'
import { Intersect } from './components/Toolbar/Intersect' import { Intersect } from './components/Toolbar/Intersect'
import { SetHorzVertDistance } from './components/Toolbar/SetHorzVertDistance' 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 { ConvertToVariable } from './components/Toolbar/ConvertVariable'
import { SetAbsDistance } from './components/Toolbar/SetAbsDistance' import { SetAbsDistance } from './components/Toolbar/SetAbsDistance'
import { SetAngleBetween } from './components/Toolbar/setAngleBetween' import { SetAngleBetween } from './components/Toolbar/SetAngleBetween'
export const Toolbar = () => { export const Toolbar = () => {
const { const {

View File

@ -79,6 +79,7 @@ function stringSplice(str: string, index: number, count: number, add: string) {
return str.slice(0, index) + (add || '') + str.slice(index + count) return str.slice(0, index) + (add || '') + str.slice(index + count)
} }
// what a terriable name
export function useCalc({ export function useCalc({
value, value,
initialVariableName: valueName = '', initialVariableName: valueName = '',

View File

@ -9,6 +9,7 @@ import {
import { import {
getNodePathFromSourceRange, getNodePathFromSourceRange,
getNodeFromPath, getNodeFromPath,
isLinesParallelAndConstrained,
} from '../../lang/queryAst' } from '../../lang/queryAst'
import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints' import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints'
import { import {
@ -17,10 +18,7 @@ import {
getTransformInfos, getTransformInfos,
} from '../../lang/std/sketchcombos' } from '../../lang/std/sketchcombos'
import { GetInfoModal } from '../SetHorVertDistanceModal' import { GetInfoModal } from '../SetHorVertDistanceModal'
import { import { createVariableDeclaration } from '../../lang/modifyAst'
createIdentifier,
createVariableDeclaration,
} from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { removeDoubleNegatives } from '../AvailableVarsHelpers'
const getModalInfo = create(GetInfoModal as any) const getModalInfo = create(GetInfoModal as any)
@ -37,9 +35,45 @@ export const Intersect = () => {
) )
const [enable, setEnable] = useState(false) const [enable, setEnable] = useState(false)
const [transformInfos, setTransformInfos] = useState<TransformInfo[]>() const [transformInfos, setTransformInfos] = useState<TransformInfo[]>()
const [forecdSelectionRanges, setForcedSelectionRanges] =
useState<typeof selectionRanges>()
useEffect(() => { useEffect(() => {
if (!ast) return 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) getNodePathFromSourceRange(ast, range)
) )
const nodes = paths.map( const nodes = paths.map(
@ -70,7 +104,8 @@ export const Intersect = () => {
const theTransforms = getTransformInfos( const theTransforms = getTransformInfos(
{ {
...selectionRanges, ...selectionRanges,
codeBasedSelections: selectionRanges.codeBasedSelections.slice(1), codeBasedSelections:
_forcedSelectionRanges.codeBasedSelections.slice(1),
}, },
ast, ast,
'intersect' 'intersect'
@ -81,7 +116,8 @@ export const Intersect = () => {
secondaryVarDecs.length === 1 && secondaryVarDecs.length === 1 &&
isAllTooltips && isAllTooltips &&
isOthersLinkedToPrimary && isOthersLinkedToPrimary &&
theTransforms.every(Boolean) theTransforms.every(Boolean) &&
_forcedSelectionRanges?.codeBasedSelections?.[1]?.type === 'line-end'
setEnable(_enableEqual) setEnable(_enableEqual)
}, [guiMode, selectionRanges]) }, [guiMode, selectionRanges])
if (guiMode.mode !== 'sketch') return null if (guiMode.mode !== 'sketch') return null
@ -89,11 +125,11 @@ export const Intersect = () => {
return ( return (
<button <button
onClick={async () => { onClick={async () => {
if (transformInfos && ast) { if (transformInfos && ast && forecdSelectionRanges) {
const { modifiedAst, tagInfo, valueUsedInTransform } = const { modifiedAst, tagInfo, valueUsedInTransform } =
transformSecondarySketchLinesTagFirst({ transformSecondarySketchLinesTagFirst({
ast: JSON.parse(JSON.stringify(ast)), ast: JSON.parse(JSON.stringify(ast)),
selectionRanges, selectionRanges: forecdSelectionRanges,
transformInfos, transformInfos,
programMemory, programMemory,
}) })
@ -129,7 +165,7 @@ export const Intersect = () => {
const { modifiedAst: _modifiedAst } = const { modifiedAst: _modifiedAst } =
transformSecondarySketchLinesTagFirst({ transformSecondarySketchLinesTagFirst({
ast, ast,
selectionRanges, selectionRanges: forecdSelectionRanges,
transformInfos, transformInfos,
programMemory, programMemory,
forceSegName: segName, forceSegName: segName,

View File

@ -1,5 +1,5 @@
import { PathToNode, ProgramMemory } from './executor' import { PathToNode, ProgramMemory, SketchGroup, SourceRange } from './executor'
import { Selection } from '../useStore' import { Selection, TooTip } from '../useStore'
import { import {
BinaryExpression, BinaryExpression,
Program, Program,
@ -13,6 +13,13 @@ import {
Identifier, Identifier,
} from './abstractSyntaxTree' } from './abstractSyntaxTree'
import { createIdentifier, splitPathAtLastIndex } from './modifyAst' 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>( export function getNodeFromPath<T>(
node: Program, node: Program,
@ -242,7 +249,7 @@ export function getNodePathFromSourceRange(
sourceRange: Selection['range'], sourceRange: Selection['range'],
previousPath: PathToNode = [['body', '']] previousPath: PathToNode = [['body', '']]
): PathToNode { ): PathToNode {
const [start, end] = sourceRange const [start, end] = sourceRange || []
let path: PathToNode = [...previousPath] let path: PathToNode = [...previousPath]
const _node = { ...node } const _node = { ...node }
@ -409,3 +416,74 @@ export function isValueZero(val?: Value): boolean {
Number(val.argument.value) === 0) 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],
}
}
}

View File

@ -1351,7 +1351,7 @@ export function addNewSketchLn({
const { from } = getSketchSegmentFromSourceRange(sketch, [ const { from } = getSketchSegmentFromSourceRange(sketch, [
defaultLine.start, defaultLine.start,
defaultLine.end, defaultLine.end,
]) ]).segment
return updateArgs({ return updateArgs({
node, node,
previousProgramMemory, previousProgramMemory,

View File

@ -386,7 +386,7 @@ show(part001)`
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange( const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
programMemory.root['part001'] as SketchGroup, programMemory.root['part001'] as SketchGroup,
[index, index] [index, index]
) ).segment
expect(segment).toEqual({ expect(segment).toEqual({
type: 'toPoint', type: 'toPoint',
to: [5.62, 1.79], to: [5.62, 1.79],
@ -399,7 +399,7 @@ show(part001)`
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange( const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
programMemory.root['part001'] as SketchGroup, programMemory.root['part001'] as SketchGroup,
[index, index] [index, index]
) ).segment
expect(segment).toEqual({ type: 'base', to: [0, 0.04], from: [0, 0.04] }) expect(segment).toEqual({ type: 'base', to: [0, 0.04], from: [0, 0.04] })
}) })
}) })

View File

@ -11,7 +11,10 @@ import { InternalFn } from './stdTypes'
export function getSketchSegmentFromSourceRange( export function getSketchSegmentFromSourceRange(
sketchGroup: SketchGroup, sketchGroup: SketchGroup,
[rangeStart, rangeEnd]: SourceRange [rangeStart, rangeEnd]: SourceRange
): SketchGroup['value'][number] { ): {
segment: SketchGroup['value'][number]
index: number
} {
const startSourceRange = sketchGroup.start?.__geoMeta.sourceRange const startSourceRange = sketchGroup.start?.__geoMeta.sourceRange
if ( if (
startSourceRange && startSourceRange &&
@ -19,14 +22,18 @@ export function getSketchSegmentFromSourceRange(
startSourceRange[1] >= rangeEnd && startSourceRange[1] >= rangeEnd &&
sketchGroup.start sketchGroup.start
) )
return sketchGroup.start return { segment: sketchGroup.start, index: -1 }
const line = sketchGroup.value.find( const lineIndex = sketchGroup.value.findIndex(
({ __geoMeta: { sourceRange } }) => ({ __geoMeta: { sourceRange } }) =>
sourceRange[0] <= rangeStart && sourceRange[1] >= rangeEnd sourceRange[0] <= rangeStart && sourceRange[1] >= rangeEnd
) )
const line = sketchGroup.value[lineIndex]
if (!line) throw new Error('could not find matching line') if (!line) throw new Error('could not find matching line')
return line return {
segment: line,
index: lineIndex,
}
} }
export const segLen: InternalFn = ( export const segLen: InternalFn = (

View File

@ -1422,9 +1422,10 @@ export function transformAstSketchLines({
const sketchGroup = programMemory.root?.[varName] const sketchGroup = programMemory.root?.[varName]
if (!sketchGroup || sketchGroup.type !== 'sketchGroup') if (!sketchGroup || sketchGroup.type !== 'sketchGroup')
throw new Error('not a sketch group') throw new Error('not a sketch group')
const seg = getSketchSegmentFromSourceRange(sketchGroup, range) const seg = getSketchSegmentFromSourceRange(sketchGroup, range).segment
const referencedSegment = referencedSegmentRange const referencedSegment = referencedSegmentRange
? getSketchSegmentFromSourceRange(sketchGroup, referencedSegmentRange) ? getSketchSegmentFromSourceRange(sketchGroup, referencedSegmentRange)
.segment
: sketchGroup.value.find((path) => path.name === _referencedSegmentName) : sketchGroup.value.find((path) => path.name === _referencedSegmentName)
const { to, from } = seg const { to, from } = seg
const { modifiedAst, valueUsedInTransform } = replaceSketchLine({ const { modifiedAst, valueUsedInTransform } = replaceSketchLine({