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 { 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 {
|
||||||
|
@ -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 = '',
|
||||||
|
@ -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,
|
||||||
|
@ -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],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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] })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -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 = (
|
||||||
|
@ -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({
|
||||||
|
Reference in New Issue
Block a user