Skip to content
This repository has been archived by the owner on Mar 4, 2024. It is now read-only.

Commit

Permalink
feat: updated the arc positioning algorithm to place all the arcs on …
Browse files Browse the repository at this point in the history
…the minimum amount of layers
  • Loading branch information
prescientmoon committed Jun 25, 2020
1 parent 1a95420 commit abd9208
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 28 deletions.
6 changes: 3 additions & 3 deletions src/Component/Editor.purs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ data Action
| SelectFunction (Maybe FunctionName)
| CreateNode FunctionName
| StartFunctionCreation
| RemoveConnection NodeId (Tuple NodeId Int) Event
| RemoveConnection (Tuple NodeId Int) Event
| SetRuntimeValue FunctionName NodeId RuntimeValue
| TogglePanel
| ChangeInputCount FunctionName Int
Expand Down Expand Up @@ -207,8 +207,8 @@ component =
handleAction LoadScene
StartFunctionCreation -> do
void $ query (SProxy :: _ "tree") unit $ tell TreeC.StartCreation
RemoveConnection from to event -> do
modify_ $ removeConnection from to
RemoveConnection to event -> do
modify_ $ removeConnection to
SetRuntimeValue functionName nodeId runtimeValue -> do
modify_ $ setRuntimeValue functionName nodeId runtimeValue
ChangeInputCount function amount -> do
Expand Down
12 changes: 7 additions & 5 deletions src/Data/Editor/State.purs
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,13 @@ generateUnconnectableInputs output state = Set.map (\(Tuple id index) -> { id, i

-- Generates a list of outputs which can't be connected
generateUnconnectableOutputs :: forall a s m. Tuple NodeId Int -> State a s m -> Set.Set NodeId
generateUnconnectableOutputs input state = Set.filter (\outputId -> not $ canConnect outputId input state) outputs
generateUnconnectableOutputs input state = Set.filter (\outputId -> not $ canConnect outputId input state') outputs
where
keys = maybe mempty Map.keys $ preview _currentNodes state
state' = removeConnection input state

outputNode = preview (_currentNodeGroup <<< _Just <<< _NodeGroupOutput) state
keys = maybe mempty Map.keys $ preview _currentNodes state'

outputNode = preview (_currentNodeGroup <<< _Just <<< _NodeGroupOutput) state'

outputs = case outputNode of
Just id -> Set.difference keys $ Set.singleton id
Expand Down Expand Up @@ -367,8 +369,8 @@ initializeFunction name state =
modify_ compile

-- Remove a conenction from the current function
removeConnection :: forall a s m. NodeId -> Tuple NodeId Int -> State a s m -> State a s m
removeConnection from (Tuple toId toIndex) state = compile state'
removeConnection :: forall a s m. Tuple NodeId Int -> State a s m -> State a s m
removeConnection (Tuple toId toIndex) state = compile state'
where
state' = set (_atCurrentNode toId <<< _nodeInput toIndex) Nothing state

Expand Down
34 changes: 23 additions & 11 deletions src/typescript/arcs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,16 @@ const full = <T>(inner: T): T & IArc => ({
*
* @param arcs The array to search in
*/
const collectIntersections = <T extends IArc>(arcs: T[]) => {
const result: Set<T> = new Set()

const firstIntersection = <T extends IArc>(arcs: T[]): [T, T] | null => {
for (const arc1 of arcs) {
for (const arc2 of arcs) {
if (arc1 !== arc2 && intersect(arc1, arc2)) {
result.add(arc1).add(arc2)
return [arc1, arc2]
}
}
}

return [...result]
return null
}

/**
Expand All @@ -96,13 +94,25 @@ const collectIntersections = <T extends IArc>(arcs: T[]) => {
const moveIntersections = <T extends IArc>(
arcs: T[]
): { newLayer: T[]; oldLayer: T[] } => {
const intersections = collectIntersections(arcs)
const intersection = firstIntersection(arcs)

if (intersection !== null) {
const nextAttempt0 = moveIntersections(
arcs.filter((arc) => arc !== intersection[0])
)

const nextAttempt1 = moveIntersections(
arcs.filter((arc) => arc !== intersection[1])
)

const [next, moved] =
nextAttempt0.oldLayer.length > nextAttempt1.oldLayer.length
? [nextAttempt0, intersection[0]]
: [nextAttempt1, intersection[1]]

if (intersections.length) {
const moved = moveIntersections(intersections.slice(1))
return {
...moved,
newLayer: [intersections[0], ...moved.newLayer]
oldLayer: next.oldLayer,
newLayer: [moved, ...next.newLayer]
}
} else {
return { newLayer: [], oldLayer: arcs }
Expand Down Expand Up @@ -286,5 +296,7 @@ export const placeInputs = <T extends { output: NodeId | null }>(
connectedLayered[index] = fillWith(unconnected, connectedLayered[index])
}

return connectedLayered.map((arr) => arr.map((arc) => rotate(HALF_PI, arc)))
return connectedLayered
.map((arr) => arr.map((arc) => rotate(HALF_PI, arc)))
.reverse()
}
20 changes: 15 additions & 5 deletions src/typescript/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,16 +225,21 @@ export const renderScene = (
const matrix = getTransform(ctx, cache)

const nodes: IHiccupShape[] = [...cache.zOrder]
.map((id) => cache.nodes.get(id)!)
.flatMap(({ inputs, output, background, connections, lastState }) => [
.map((id) => ({ ...cache.nodes.get(id)!, id }))
.flatMap(({ inputs, output, background, connections, id }) => [
...(background.attribs!.fill ? [background] : []),
...inputs.flatMap((input, index): IHiccupShape[] => {
if (input.type === Type.CIRCLE) return [input]

const arc = g.pathFromCubics(g.asCubic(input))
const connection = connections[index]

if (connection.attribs!.connected) {
const isPreviewed =
cache.connection._type === PartialKind.Input &&
cache.connection.id === id &&
cache.connection.index === index

if (connection.attribs!.connected && !isPreviewed) {
return [connection, arc]
}

Expand Down Expand Up @@ -571,8 +576,13 @@ export const onMouseMove = (
cache.selectedOutput.output.r = nodeOutputRadius.normal
}

cache.selectedOutput = target.node
target.node!.output.r = nodeOutputRadius.onHover
if (
cache.connection._type !== PartialKind.Input ||
!cache.connection.unconnectable.has(target.id)
) {
cache.selectedOutput = target.node
target.node!.output.r = nodeOutputRadius.onHover
}
}

// On hover effect for inputs
Expand Down
16 changes: 12 additions & 4 deletions src/typescript/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from "./types/Node"
import * as Native from "./render"
import * as Arc from "./arcs"
import type { Vec2Like } from "@thi.ng/vectors"
import { Vec2Like, sub2, polar2, cartesian2 } from "@thi.ng/vectors"
import { inputLayerOffset, nodeRadius, arcSpacing } from "./constants"
import * as g from "@thi.ng/geom"
import { TAU } from "@thi.ng/math"
Expand Down Expand Up @@ -115,14 +115,22 @@ export const refreshInputArcsImpl = (
continue
}

const input = node.inputs[index]
const start = cache.nodes.get(connectedTo)!.position
const input = node.inputs[index] as g.Arc
const startNode = cache.nodes.get(connectedTo)!
const start = startNode.position

// TODO: handle the rare cases when input color != output color
connection.attribs!.connected = true
connection.attribs!.stroke = input.attribs!.stroke

connection.points[0] = start
connection.points[0] = sub2(
[],
start,
cartesian2(
[],
[(input.start + input.end) / 2, startNode.output!.r].reverse()
)
)
connection.points[1] = g.closestPoint(input, start)!
}
}
Expand Down

0 comments on commit abd9208

Please sign in to comment.