Merge in yhp19's code

This commit is contained in:
Aadi Desai 2022-03-14 14:21:49 +00:00
parent 72955fe300
commit 89b75418b9
No known key found for this signature in database
GPG key ID: CFFFE425830EF4D9
2 changed files with 184 additions and 241 deletions

View file

@ -21,6 +21,9 @@ let minSegLen = 5.
type Orientation = Horizontal | Vertical type Orientation = Horizontal | Vertical
/// In Wire.Rotation: PosY: 0, PosX: 90, NegY: 180, NegX: 270 degree clockwise rotation
type Direction = PosX | PosY | NegX | NegY
type SnapPosition = High | Mid | Low type SnapPosition = High | Mid | Low
/// Absolute Segment /// Absolute Segment
@ -115,6 +118,9 @@ type Wire =
OutputPort: OutputPortId OutputPort: OutputPortId
Color: HighLightColor Color: HighLightColor
Width: int Width: int
Rotation: Direction
/// True = Reflected along y axis , False = Not Reflected
YReflect: bool
Segments: ASeg list Segments: ASeg list
} }
@ -150,11 +156,86 @@ type Msg =
| DragWire of ConnectionId * MouseT | DragWire of ConnectionId * MouseT
| ColorWires of ConnectionId list * HighLightColor | ColorWires of ConnectionId list * HighLightColor
| ErrorWires of ConnectionId list | ErrorWires of ConnectionId list
| ResetJumps of ConnectionId list
| MakeJumps of ConnectionId list
| ResetModel // For Issie Integration | ResetModel // For Issie Integration
| LoadConnections of Connection list // For Issie Integration | LoadConnections of Connection list // For Issie Integration
//----------------------------Helper Functions-----------------------------------//
/// Transform a wire with its RISegs list to a list of ASegs
let riSegWireToASegs (wire:Wire) =
let getStartEnd (seg:RISeg)=
match seg.Dir with
| Horizontal -> (seg.Start,{seg.Start with X = seg.Start.X+seg.Length})
| Vertical -> (seg.Start,{seg.Start with Y = seg.Start.Y+seg.Length})
let rotate (pos:XYPos) =
match wire.Rotation with
| PosX -> {pos with X = pos.Y ; Y = -pos.X}
| NegX -> {pos with X = -pos.Y ; Y = pos.X}
| PosY -> pos
| NegY -> {pos with X = -pos.X; Y = -pos.Y}
let yReflect (pos:XYPos) =
if wire.YReflect then
{pos with X = -pos.X}
else
pos
let check_Orientation (pos: XYPos * XYPos)=
let pos1 = fst pos
let pos2 = snd pos
if abs (pos1.X - pos2.X) <= XYPos.epsilon
then Vertical
else Horizontal
let lstOfPos =
List.map getStartEnd (List.map aSegToRISeg wire.Segments)
|> List.map (fun (s,e) -> (rotate s, rotate e))
|> List.map (fun (s,e) -> (yReflect s, yReflect e))
let posToASegs (pos: XYPos * XYPos) (riSeg: RISeg) : ASeg =
{
Id = riSeg.Id;
Index = riSeg.Index;
Start = fst pos;
End = snd pos;
Dir = check_Orientation pos;
HostId = riSeg.HostId;
JumpCoordinateListA = List.map(fun (len, id) -> (len + (fst pos).X) , id) riSeg.JumpDistanceListRI;
Draggable = riSeg.Draggable;
ManualRoute = riSeg.ManualRoute
}
(lstOfPos,(List.map aSegToRISeg wire.Segments)) ||> List.map2 posToASegs
/// Transform a list of ASegs to RISegs list (default Rotation: PosY, YReflect: False)
let aSegListToRISegList (aSegList: ASeg list) =
let toRISeg (seg: ASeg) =
let direction, length =
let xChange = seg.End.X - seg.Start.X
let yChange = seg.End.Y - seg.Start.Y
match xChange, yChange with
| x, y when x = 0 -> Vertical, y
| x, y when y = 0 -> Horizontal, x
// default orientation for unused segments?
| x, _ -> Horizontal, x
{
Id = seg.Id
Index = seg.Index
Length = length
Start = seg.Start
Dir = direction
HostId = seg.HostId
JumpDistanceListRI = List.map(fun (x, id) -> (x - seg.Start.X) , id) seg.JumpCoordinateListA
Draggable = seg.Draggable
ManualRoute = seg.ManualRoute
}
aSegList
|> List.map toRISeg
//-------------------------Debugging functions---------------------------------// //-------------------------Debugging functions---------------------------------//
let ppSId (sId:SegmentId) = let ppSId (sId:SegmentId) =
@ -206,7 +287,7 @@ let ppSeg seg (model: Model) =
let wire = model.WX[cid] let wire = model.WX[cid]
let sg = List.find (fun (s: ASeg) -> s.Id = sid ) wire.Segments let sg = List.find (fun (s: ASeg) -> s.Id = sid ) wire.Segments
let pxy (xy: XYPos) = sprintf $"{(xy.X,xy.Y)}" let pxy (xy: XYPos) = sprintf $"{(xy.X,xy.Y)}"
sprintf $"""[{ppSId sg.Id}: {pxy sg.Start}->{pxy sg.End}]-{match sg.Dir with | Vertical -> "V" | _ -> "H"}-{sg.Index}""" sprintf $"""[{ppSId sg.Id}: {pxy sg.Start}]-{match sg.Dir with | Horizontal -> "Horizontal" | Vertical -> "Vertical" }-{sg.Index}"""
let pp segs (model: Model)= let pp segs (model: Model)=
segs segs
@ -216,7 +297,7 @@ let pp segs (model: Model)=
match List.tryFind (fun (s: ASeg) -> s.Id = sid ) wire.Segments with match List.tryFind (fun (s: ASeg) -> s.Id = sid ) wire.Segments with
| Some sg -> | Some sg ->
let pxy (xy: XYPos) = sprintf $"{(xy.X,xy.Y)}" let pxy (xy: XYPos) = sprintf $"{(xy.X,xy.Y)}"
sprintf $"""[{pxy sg.Start}->{pxy sg.End}]-{match sg.Dir with | Vertical -> "V" | _ -> "H"}-{sg.Index}""" sprintf $"""[{pxy sg.Start}]-{match sg.Dir with | Horizontal -> "Horizontal" | Vertical -> "Vertical" }-{sg.Index}"""
| None -> "XX") | None -> "XX")
|> String.concat ";" |> String.concat ";"
@ -328,10 +409,10 @@ let convertVerticesToASegs connId (isLeftToRight: bool) (xyVerticesList: XYPos l
ManualRoute = false ManualRoute = false
}) })
// TODO: native RISeg implementation
let convertVerticesToRISegs connId (isLeftToRight: bool) (verticesList: XYPos list) : RISeg list = let convertVerticesToRISegs connId (isLeftToRight: bool) (verticesList: XYPos list) : RISeg list =
convertVerticesToASegs connId isLeftToRight verticesList convertVerticesToASegs connId isLeftToRight verticesList
|> List.map aSegToRISeg |> List.map aSegToRISeg
// TODO: native RISeg implementation
/// Convert a (possibly legacy) issie Connection stored as a list of vertices to Absolute Segments /// Convert a (possibly legacy) issie Connection stored as a list of vertices to Absolute Segments
let issieVerticesToASegs connId (verticesList: (float * float) list) : ASeg list = let issieVerticesToASegs connId (verticesList: (float * float) list) : ASeg list =
@ -356,11 +437,11 @@ let issieVerticesToASegs connId (verticesList: (float * float) list) : ASeg list
printfn "Converting unknown" printfn "Converting unknown"
makeNewSegmentsFromPorts XYPosList makeNewSegmentsFromPorts XYPosList
// TODO: native RISeg implementation
/// Convert a (possibly legacy) issie Connection stored as a list of vertices to Rotation Invariant Segments /// Convert a (possibly legacy) issie Connection stored as a list of vertices to Rotation Invariant Segments
let issieVerticesToRISegs connId (verticesList: (float * float) list) : RISeg list = let issieVerticesToRISegs connId (verticesList: (float * float) list) : RISeg list =
issieVerticesToASegs connId verticesList issieVerticesToASegs connId verticesList
|> List.map aSegToRISeg |> List.map aSegToRISeg
// TODO: native RISeg implementation
//----------------------interface to Issie-----------------------// //----------------------interface to Issie-----------------------//
// Section of functions that offer an interface between our implementation and Issie // Section of functions that offer an interface between our implementation and Issie
@ -434,11 +515,11 @@ let makeInitialASegList (hostId: ConnectionId) (portCoords: XYPos * XYPos) : ASe
let xyPairs, isLeftToRight = initialWireVerticesFromPorts portCoords let xyPairs, isLeftToRight = initialWireVerticesFromPorts portCoords
xyPairs |> convertVerticesToASegs hostId isLeftToRight xyPairs |> convertVerticesToASegs hostId isLeftToRight
// TODO: native RISeg implementation
/// Initial list of rotation invariant segments based on positions of ports to be connected /// Initial list of rotation invariant segments based on positions of ports to be connected
let makeInitialRISegList (hostId: ConnectionId) (portCoords: XYPos * XYPos) : RISeg list = let makeInitialRISegList (hostId: ConnectionId) (portCoords: XYPos * XYPos) : RISeg list =
makeInitialASegList hostId portCoords makeInitialASegList hostId portCoords
|> List.map aSegToRISeg |> List.map aSegToRISeg
// TODO: native RISeg implementation
/// Render given Rotation Invariant Segment using colour and width properties given /// Render given Rotation Invariant Segment using colour and width properties given
/// Takes in 2 connecting segments to add rounded corners if appropriate /// Takes in 2 connecting segments to add rounded corners if appropriate
@ -500,7 +581,6 @@ let renderRISegAndCorner (colour: string) (width: string) (segments: RISeg * RIS
g [] segmentElements g [] segmentElements
/// Takes in a Rotation Invariant Segment List and renders the resulting React Element List, with rounded corners if appropriate /// Takes in a Rotation Invariant Segment List and renders the resulting React Element List, with rounded corners if appropriate
let renderRISegList (colour: string) (width: string) (segs: RISeg list) : ReactElement list = let renderRISegList (colour: string) (width: string) (segs: RISeg list) : ReactElement list =
let riSegs = let riSegs =
@ -587,10 +667,6 @@ let view (model: Model) (dispatch: Dispatch<Msg>) =
g [] [(g [] wires); symbols] g [] [(g [] wires); symbols]
|> TimeHelpers.instrumentInterval "WireView" start |> TimeHelpers.instrumentInterval "WireView" start
// -------------------------------------------------------------- Inigo Selwood
/// Gets the intersection point between two segments, or None if the segments /// Gets the intersection point between two segments, or None if the segments
/// don't intersect /// don't intersect
let segmentIntersectsSegmentCoordinates let segmentIntersectsSegmentCoordinates
@ -622,7 +698,6 @@ let segmentIntersectsSegmentCoordinates
else else
None None
/// Given a bounding box, find the top-left and bottom-right corners. /// Given a bounding box, find the top-left and bottom-right corners.
/// Note: finding the (min, max) vertex values seems a little redundant, but /// Note: finding the (min, max) vertex values seems a little redundant, but
/// maybe the (width, height) values can be negative? Either way, I haven't /// maybe the (width, height) values can be negative? Either way, I haven't
@ -649,7 +724,6 @@ let getTopLeftAndBottomRightCorner (box : BoundingBox) : XYPos * XYPos =
(topLeft, bottomRight) (topLeft, bottomRight)
/// Given a segment and a bounding box, evaluates: /// Given a segment and a bounding box, evaluates:
/// - (false, None) if there's no intersection /// - (false, None) if there's no intersection
/// - (true, None) if it's fully inside the box /// - (true, None) if it's fully inside the box
@ -704,7 +778,6 @@ let segmentIntersectsBoundingBoxCoordinates
else else
true, Some (List.head intersectionList) true, Some (List.head intersectionList)
/// Given a point and a segment, calculate the distance between the two /// Given a point and a segment, calculate the distance between the two
let distanceFromPointToSegment (point: XYPos) (segment: ASeg): float = let distanceFromPointToSegment (point: XYPos) (segment: ASeg): float =
@ -734,7 +807,6 @@ let distanceFromPointToSegment (point: XYPos) (segment: ASeg): float =
(abs alpha) / (sqrt beta) (abs alpha) / (sqrt beta)
/// Given the model's state and a list of wire IDs to remap, /// Given the model's state and a list of wire IDs to remap,
/// reroute those wires with default shapes between /// reroute those wires with default shapes between
let routeGivenWiresBasedOnPortPositions let routeGivenWiresBasedOnPortPositions
@ -774,7 +846,6 @@ let routeGivenWiresBasedOnPortPositions
{model with WX = newWX} {model with WX = newWX}
/// For a given connection ID and bounding box: find the segments of the wire /// For a given connection ID and bounding box: find the segments of the wire
/// that intersect the boundary (in a given model) /// that intersect the boundary (in a given model)
let getIntersectingSegments let getIntersectingSegments
@ -789,7 +860,6 @@ let getIntersectingSegments
// Filter for only the segments which intersect // Filter for only the segments which intersect
List.filter segmentFilter model.WX[wireId].Segments List.filter segmentFilter model.WX[wireId].Segments
/// Finds the closest segment in a wire to a point (using euclidean distance) /// Finds the closest segment in a wire to a point (using euclidean distance)
let getClosestSegment let getClosestSegment
(model: Model) (model: Model)
@ -800,7 +870,6 @@ let getClosestSegment
distanceFromPointToSegment pos segment distanceFromPointToSegment pos segment
List.minBy distanceToPoint (model.WX[wireId].Segments) List.minBy distanceToPoint (model.WX[wireId].Segments)
/// Gets the ID of the wire clicked /// Gets the ID of the wire clicked
/// Note: presumes we already know a wire has been clicked on, and just need /// Note: presumes we already know a wire has been clicked on, and just need
/// its ID /// its ID
@ -826,7 +895,6 @@ let getClickedSegment
| true -> (getClosestSegment model wireID position).Id | true -> (getClosestSegment model wireID position).Id
| false -> (List.head intersectingSegments).Id | false -> (List.head intersectingSegments).Id
/// Verifies that the segment is aligned with the axis it's meant to lie on /// Verifies that the segment is aligned with the axis it's meant to lie on
let checkSegmentAngle (segment: ASeg) (name: string): unit = let checkSegmentAngle (segment: ASeg) (name: string): unit =
@ -844,17 +912,14 @@ let checkSegmentAngle (segment: ASeg) (name: string): unit =
if not isAligned then if not isAligned then
printfn $"Weird segment '{name}':\n{segment}\n\n fails angle checking" printfn $"Weird segment '{name}':\n{segment}\n\n fails angle checking"
/// Checks whether a segment points to the left /// Checks whether a segment points to the left
let segPointsLeft (segment: ASeg): bool = let segPointsLeft (segment: ASeg): bool =
(abs segment.Start.X > abs segment.End.X) && (segment.Dir = Horizontal) (abs segment.Start.X > abs segment.End.X) && (segment.Dir = Horizontal)
/// Checks the segment's length along the X axis /// Checks the segment's length along the X axis
let segXDelta (segment: ASeg): float = let segXDelta (segment: ASeg): float =
(abs segment.End.X) - (abs segment.Start.X) (abs segment.End.X) - (abs segment.Start.X)
/// Given two segments which are joined at a given position, change the X /// Given two segments which are joined at a given position, change the X
/// coordinate of that coordinate (compensating for negative values) /// coordinate of that coordinate (compensating for negative values)
let moveXJoinPos let moveXJoinPos
@ -873,7 +938,6 @@ let moveXJoinPos
{segment2 with Start = changeXKeepingSign segment2.Start} {segment2 with Start = changeXKeepingSign segment2.Start}
] ]
/// Picks the outermost segment (usually segment 1, unless isAtEnd [of wire]) /// Picks the outermost segment (usually segment 1, unless isAtEnd [of wire])
/// and ensures it's at least a Wire.stickLength long by moving the point where /// and ensures it's at least a Wire.stickLength long by moving the point where
/// it meets the other (innermost) segment /// it meets the other (innermost) segment
@ -916,7 +980,6 @@ let changeLengths
else else
[segment1; segment2] [segment1; segment2]
/// Called for segments [1:5] -- if they're vertical and can move horizontally. /// Called for segments [1:5] -- if they're vertical and can move horizontally.
/// Returns the distance value once it's been moderated to prevent wires /// Returns the distance value once it's been moderated to prevent wires
/// moving into components. /// moving into components.
@ -967,7 +1030,6 @@ let getSafeDistanceForMove
| 3 -> distance |> max minimumDistance |> min maximumDistance | 3 -> distance |> max minimumDistance |> min maximumDistance
| _ -> distance | _ -> distance
/// Remove pairs of adjacent segments which are aligned but not of the same /// Remove pairs of adjacent segments which are aligned but not of the same
/// sign /// sign
let removeRedundantSegments (segments: ASeg list) = let removeRedundantSegments (segments: ASeg list) =
@ -1021,7 +1083,6 @@ let removeRedundantSegments (segments: ASeg list) =
@ segments[2..4] @ segments[2..4]
@ reduce segments[5] segments[6] @ reduce segments[5] segments[6]
/// Moves a wire segment a given amount perpendicular to its orientation. /// Moves a wire segment a given amount perpendicular to its orientation.
/// Used to manually adjust routing by dragging with the mouse. /// Used to manually adjust routing by dragging with the mouse.
let moveSegment (segment: ASeg) (distance:float) (model:Model) = let moveSegment (segment: ASeg) (distance:float) (model:Model) =
@ -1095,7 +1156,6 @@ let moveSegment (segment: ASeg) (distance:float) (model:Model) =
// Create a new wire with the moved and reduced segments // Create a new wire with the moved and reduced segments
{wire with Segments = newSegments} {wire with Segments = newSegments}
/// Initialisatiton with no wires /// Initialisatiton with no wires
let init () = let init () =
let symbols, _ = Symbol.init() let symbols, _ = Symbol.init()
@ -1115,7 +1175,6 @@ let init () =
(model, Cmd.none) (model, Cmd.none)
/// Returns the wires connected to a list of components /// Returns the wires connected to a list of components
let getConnectedWires (model : Model) (componentIDs : ComponentId list) = let getConnectedWires (model : Model) (componentIDs : ComponentId list) =
@ -1131,7 +1190,6 @@ let getConnectedWires (model : Model) (componentIDs : ComponentId list) =
|> List.map (fun wire -> wire.Id) |> List.map (fun wire -> wire.Id)
|> List.distinct |> List.distinct
/// Returns 3 tuples: /// Returns 3 tuples:
/// - wires connected only to inputs /// - wires connected only to inputs
/// - those connected only to outputs /// - those connected only to outputs
@ -1172,7 +1230,6 @@ let filterWiresByCompMoved (model: Model) (componentIDs: ComponentId list) =
(inputWires, outputWires, fullyConnected) (inputWires, outputWires, fullyConnected)
/// Returns a newly autorouted wire given a model and wire /// Returns a newly autorouted wire given a model and wire
let autorouteWire (model: Model) (wire: Wire): Wire = let autorouteWire (model: Model) (wire: Wire): Wire =
@ -1187,7 +1244,6 @@ let autorouteWire (model: Model) (wire: Wire): Wire =
Segments = makeInitialASegList wire.Id locations Segments = makeInitialASegList wire.Id locations
} }
/// Reverse the segment order, as well as (start, end) coordinates /// Reverse the segment order, as well as (start, end) coordinates
let revSegments (segments: ASeg list) = let revSegments (segments: ASeg list) =
let invert (segment: ASeg) : ASeg = let invert (segment: ASeg) : ASeg =
@ -1200,7 +1256,6 @@ let revSegments (segments: ASeg list) =
List.rev segments List.rev segments
|> List.map invert |> List.map invert
// ==================================================================================================================== // ====================================================================================================================
// //
// WIRE SEGMENTS FOR ROUTING // WIRE SEGMENTS FOR ROUTING
@ -1228,7 +1283,6 @@ let revSegments (segments: ASeg list) =
// //
// ====================================================================================================================== // ======================================================================================================================
/// Create a position, the sum of two other positions /// Create a position, the sum of two other positions
let inline addPosPos (position1: XYPos) (position2: XYPos): XYPos = let inline addPosPos (position1: XYPos) (position2: XYPos): XYPos =
{ {
@ -1236,7 +1290,6 @@ let inline addPosPos (position1: XYPos) (position2: XYPos): XYPos =
Y = position1.Y + position2.Y Y = position1.Y + position2.Y
} }
/// Applies a mover function to the end of a segment at a given index /// Applies a mover function to the end of a segment at a given index
let inline moveEnd (mover: XYPos -> XYPos) (setIndex: int) = let inline moveEnd (mover: XYPos -> XYPos) (setIndex: int) =
@ -1251,7 +1304,6 @@ let inline moveEnd (mover: XYPos -> XYPos) (setIndex: int) =
List.mapi setEndAtIndex List.mapi setEndAtIndex
/// Applies a mover function to the start of a segment at a given index /// Applies a mover function to the start of a segment at a given index
let inline moveStart (mover: XYPos -> XYPos) (setIndex: int) = let inline moveStart (mover: XYPos -> XYPos) (setIndex: int) =
@ -1266,7 +1318,6 @@ let inline moveStart (mover: XYPos -> XYPos) (setIndex: int) =
List.mapi setStartAtIndex List.mapi setStartAtIndex
/// Applies a mover function to the (start, end) of a segment at a given index /// Applies a mover function to the (start, end) of a segment at a given index
let inline moveAll (mover: XYPos -> XYPos) (setIndex: int) = let inline moveAll (mover: XYPos -> XYPos) (setIndex: int) =
@ -1282,7 +1333,6 @@ let inline moveAll (mover: XYPos -> XYPos) (setIndex: int) =
List.mapi setAllAtIndex List.mapi setAllAtIndex
/// Applies (X, Y) transformations to a point /// Applies (X, Y) transformations to a point
let transformXY let transformXY
(xTransform: float -> float) (xTransform: float -> float)
@ -1295,7 +1345,6 @@ let transformXY
Y = yTransform point.Y Y = yTransform point.Y
} }
/// Applies (X, Y) transformations to the start and end of a segment /// Applies (X, Y) transformations to the start and end of a segment
let transformSeg let transformSeg
(xTransform: float -> float) (xTransform: float -> float)
@ -1310,14 +1359,12 @@ let transformSeg
End = transform segment.End End = transform segment.End
} }
/// Gets a tuple, the pair of directions in each axis /// Gets a tuple, the pair of directions in each axis
let topology (position1: XYPos) (position2: XYPos): int * int = let topology (position1: XYPos) (position2: XYPos): int * int =
let delta (point0: float) (point1: float): int = let delta (point0: float) (point1: float): int =
sign (abs point0 - abs point1) sign (abs point0 - abs point1)
(delta position1.X position2.X), (delta position1.Y position2.Y) (delta position1.X position2.X), (delta position1.Y position2.Y)
/// Performs a partial autoroute. Will fail (returning None) if a full /// Performs a partial autoroute. Will fail (returning None) if a full
/// autoroute is needed -- or if there are manually dragged segments in the /// autoroute is needed -- or if there are manually dragged segments in the
/// way. /// way.
@ -1428,7 +1475,6 @@ let partialAutoRoute
|> Option.bind checkTopology |> Option.bind checkTopology
|> Option.bind preEndScale |> Option.bind preEndScale
/// Returns the new positions keeping manual coordinates negative, and auto /// Returns the new positions keeping manual coordinates negative, and auto
/// coordinates positive /// coordinates positive
let negXYPos (position: XYPos) (difference: XYPos): XYPos = let negXYPos (position: XYPos) (difference: XYPos): XYPos =
@ -1442,7 +1488,6 @@ let negXYPos (position: XYPos) (difference: XYPos): XYPos =
else else
newPosition newPosition
/// Moves a wire by a specified amount by adding a XYPos to each start and end /// Moves a wire by a specified amount by adding a XYPos to each start and end
/// point of each segment /// point of each segment
let moveWire (wire : Wire) (difference : XYPos): Wire = let moveWire (wire : Wire) (difference : XYPos): Wire =
@ -1459,7 +1504,6 @@ let moveWire (wire : Wire) (difference : XYPos): Wire =
Segments = List.map transformer wire.Segments Segments = List.map transformer wire.Segments
} }
/// Re-routes a wire in the model when its ports have moved. Tries to preserve /// Re-routes a wire in the model when its ports have moved. Tries to preserve
/// manual routing when it makes sense to do so -- otherwise, use auto-routing. /// manual routing when it makes sense to do so -- otherwise, use auto-routing.
let updateWire (model: Model) (wire: Wire) (isInput: bool) = let updateWire (model: Model) (wire: Wire) (isInput: bool) =
@ -1485,112 +1529,38 @@ let updateWire (model: Model) (wire: Wire) (isInput: bool) =
|> Option.map (fun segs -> {wire with Segments = segs}) |> Option.map (fun segs -> {wire with Segments = segs})
|> Option.defaultValue (autorouteWire model wire) |> Option.defaultValue (autorouteWire model wire)
// ------------------------------------------------------------ / Inigo Selwood
let makeAllJumps (wiresWithNoJumps: ConnectionId list) (model: Model) =
let mutable newWX = model.WX
// Arrays are faster to check than lists
let wiresWithNoJumpsA = List.toArray wiresWithNoJumps
let changeJumps wid index jumps =
let jumps = List.sortDescending jumps
let changeSegment segs =
List.mapi (fun i x -> if i <> index then x else { x with JumpCoordinateListA = jumps }) segs
newWX <- Map.add wid { newWX[wid] with Segments = changeSegment newWX[wid].Segments } newWX
let segs =
model.WX
|> Map.toArray
|> Array.map (fun (_wid, w) -> List.toArray w.Segments)
for w1 in 0 .. segs.Length - 1 do
for h in segs[w1] do
if h.Dir = Horizontal then
// work out what jumps this segment should have
let mutable jumps: (float * SegmentId) list = []
if not (Array.contains h.HostId wiresWithNoJumpsA) then
for w2 in 0 .. segs.Length - 1 do
// everything inside the inner loop should be very highly optimised
// it is executed n^2 time where n is the number of segments (maybe 5000)
// the abs here are because segment coordinates my be negated to indicate manual routing
for v in segs[w2] do
if not (Array.contains v.HostId wiresWithNoJumpsA) then
match v.Dir with
| Vertical ->
let x, x1, x2 = abs v.Start.X, abs h.Start.X, abs h.End.X
let y, y1, y2 = abs h.Start.Y, abs v.Start.Y, abs v.End.Y
let xhi, xlo = max x1 x2, min x1 x2
let yhi, ylo = max y1 y2, min y1 y2
//printfn $"{[xlo;x;xhi]}, {[ylo;y;yhi]}"
if x < xhi - 5.0 && x > xlo + 5.0 && y < yhi - 5.0 && y > ylo + 5.0 then
//printfn "found a jump!"
jumps <- (x, v.Id) :: jumps
| _ -> ()
// compare jumps with what segment now has, and change newWX if need be
// note that if no change is needed we do not update WX
// simple cases are done without sort for speed, proably not necessary!
// The jump list is sorted in model to enable easier rendering of segments
match jumps, h.JumpCoordinateListA with
| [], [] -> ()
| [ a ], [ b ] when a <> b -> changeJumps h.HostId h.Index jumps
| [], _ -> changeJumps h.HostId h.Index jumps
| _, [] -> // in this case we need to sort the jump list
changeJumps h.HostId h.Index (List.sort jumps)
| newJumps, oldJ ->
let newJ = List.sort newJumps
// oldJ is already sorted (we only ever write newJ back to model)
if newJ <> oldJ then changeJumps h.HostId h.Index newJumps else ()
{ model with WX = newWX }
let updateWireSegmentJumps (_wireList: ConnectionId list) (wModel: Model) : Model =
let startT = TimeHelpers.getTimeMs()
let model = makeAllJumps [] wModel
TimeHelpers.instrumentTime "UpdateJumps" startT
model
/// This function updates the wire model by removing from the stored lists of intersections
/// all those generated by wireList wires.
/// intersetcions are stored in maps on the model and on the horizontal segments containing the jumps
let resetWireSegmentJumps (wireList: ConnectionId list) (wModel: Model) : Model =
makeAllJumps wireList wModel
/// Re-routes the wires in the model based on a list of components that have been altered. /// Re-routes the wires in the model based on a list of components that have been altered.
/// If the wire input and output ports are both in the list of moved components, does not re-route wire but instead translates it. /// If the wire input and output ports are both in the list of moved components, does not re-route wire but instead translates it.
/// Keeps manual wires manual (up to a point). /// Keeps manual wires manual (up to a point).
/// Otherwise it will auto-route wires connected to components that have moved /// Otherwise it will auto-route wires connected to components that have moved
let updateWires (model: Model) (compIdList: ComponentId list) (diff: XYPos) = let updateWires (model: Model) (compIdList: ComponentId list) (diff: XYPos) =
let (inputWires, outputWires, fullyConnectedWires) = filterWiresByCompMoved model compIdList
let (inputWires, outputWires, fullyConnected) = filterWiresByCompMoved model compIdList
let newWires = let newWires =
model.WX model.WX
|> Map.toList |> Map.toList
|> List.map (fun (cId, wire) -> |> List.map (fun (cId, wire) ->
if List.contains cId fullyConnected //Translate wires that are connected to moving components on both sides if List.contains cId fullyConnectedWires then // Translate wires that are connected to moving components on both sides
then (cId, moveWire wire diff) (cId, moveWire wire diff)
elif List.contains cId inputWires //Only route wires connected to ports that moved for efficiency elif List.contains cId inputWires then // Only route wires connected to ports that moved for efficiency
then (cId, updateWire model wire true) (cId, updateWire model wire true)
elif List.contains cId outputWires elif List.contains cId outputWires then
then (cId, updateWire model wire false) (cId, updateWire model wire false)
else (cId, wire)) else
(cId, wire))
|> Map.ofList |> Map.ofList
{model with WX = newWires} {model with WX = newWires}
/// /// Update the Model according to the message received, return a tuple of new Model and Msg
let update (msg : Msg) (model : Model) : Model*Cmd<Msg> = let update (msg : Msg) (model : Model) : Model * Cmd<Msg> =
match msg with match msg with
| Symbol sMsg -> | Symbol sMsg ->
let sm,sCmd = Symbol.update sMsg model.Symbol let sm,sCmd = Symbol.update sMsg model.Symbol
{model with Symbol=sm}, Cmd.map Symbol sCmd {model with Symbol=sm}, Cmd.map Symbol sCmd
| UpdateWires (componentIdList, diff) -> updateWires model componentIdList diff, Cmd.none
| UpdateWires (componentIdList, diff) ->
updateWires model componentIdList diff, Cmd.none
| AddWire ( (inputId, outputId) : (InputPortId * OutputPortId) ) -> | AddWire ( (inputId, outputId) : (InputPortId * OutputPortId) ) ->
let portOnePos, portTwoPos = Symbol.getTwoPortLocations model.Symbol inputId outputId // TODO: Symbol fn in BusWire let portOnePos, portTwoPos = Symbol.getTwoPortLocations model.Symbol inputId outputId // TODO: Symbol fn in BusWire
@ -1604,16 +1574,17 @@ let update (msg : Msg) (model : Model) : Model*Cmd<Msg> =
OutputPort = outputId OutputPort = outputId
Color = HighLightColor.DarkSlateGrey Color = HighLightColor.DarkSlateGrey
Width = 1 Width = 1
Rotation = PosY
YReflect = false
Segments = segmentList Segments = segmentList
} }
let wireAddedMap = Map.add newWire.Id newWire model.WX let wireAddedMap = Map.add newWire.Id newWire model.WX
let newModel = updateWireSegmentJumps [wireId] {model with WX = wireAddedMap} let newModel = {model with WX = wireAddedMap}
newModel, Cmd.ofMsg BusWidths newModel, Cmd.ofMsg BusWidths
| BusWidths -> | BusWidths ->
let processConWidths (connWidths: ConnectionsWidth) = let processConWidths (connWidths: ConnectionsWidth) =
let addWireWidthFolder (wireMap: Map<ConnectionId, Wire>) _ wire = let addWireWidthFolder (wireMap: Map<ConnectionId, Wire>) _ wire =
let width = let width =
@ -1629,10 +1600,10 @@ let update (msg : Msg) (model : Model) : Model*Cmd<Msg> =
let symbol = m[symId] let symbol = m[symId]
match symbol.Compo.Type with match symbol.Compo.Type with
| SplitWire _n -> | SplitWire _ ->
match inPort.PortNumber with match inPort.PortNumber with
| Some 0 -> {symbol with InWidth0 = Some wire.Width} | Some 0 -> {symbol with InWidth0 = Some wire.Width}
| x -> failwithf $"What? wire found with input port {x} other than 0 connecting to SplitWire" | x -> failwithf $"Error: wire found with input port {x} other than 0 connecting to SplitWire"
|> (fun sym -> Map.add symId sym m) |> (fun sym -> Map.add symId sym m)
| MergeWires -> | MergeWires ->
match inPort.PortNumber with match inPort.PortNumber with
@ -1640,24 +1611,20 @@ let update (msg : Msg) (model : Model) : Model*Cmd<Msg> =
Map.add symId {symbol with InWidth0 = Some wire.Width} m Map.add symId {symbol with InWidth0 = Some wire.Width} m
| Some 1 -> | Some 1 ->
Map.add symId {symbol with InWidth1 = Some wire.Width} m Map.add symId {symbol with InWidth1 = Some wire.Width} m
| x -> failwithf $"What? wire found with input port {x} other than 0 or 1 connecting to MergeWires" | x -> failwithf $"Error: wire found with input port {x} other than 0 or 1 connecting to MergeWires"
| _ -> m | _ -> m
let newWX = ((Map.empty, model.WX) ||> Map.fold addWireWidthFolder) let newWX = ((Map.empty, model.WX) ||> Map.fold addWireWidthFolder)
let symbolsWithWidths = (model.Symbol.Symbols, newWX) ||> Map.fold addSymbolWidthFolder
let symbolsWithWidths =
(model.Symbol.Symbols, newWX) ||> Map.fold addSymbolWidthFolder
{ model with { model with
WX = newWX; Notifications = None ; WX = newWX; Notifications = None ;
ErrorWires=[]; ErrorWires=[];
Symbol = {model.Symbol with Symbols = symbolsWithWidths}}, Cmd.none Symbol = {model.Symbol with Symbols = symbolsWithWidths}
}, Cmd.none
let canvasState = (Symbol.extractComponents model.Symbol, extractConnections model ) let canvasState = (Symbol.extractComponents model.Symbol, extractConnections model )
match BusWidthInferer.inferConnectionsWidth canvasState with match BusWidthInferer.inferConnectionsWidth canvasState with
| Ok connWidths -> | Ok connWidths ->
processConWidths connWidths processConWidths connWidths
@ -1687,7 +1654,7 @@ let update (msg : Msg) (model : Model) : Model*Cmd<Msg> =
let newWX = let newWX =
model.WX model.WX
|> Map.map |> Map.map
(fun id wire -> ( fun id wire ->
if List.contains id model.ErrorWires then if List.contains id model.ErrorWires then
if List.contains id connectionIds then if List.contains id connectionIds then
{wire with Color = HighLightColor.Brown} {wire with Color = HighLightColor.Brown}
@ -1702,11 +1669,10 @@ let update (msg : Msg) (model : Model) : Model*Cmd<Msg> =
{model with WX = newWX}, Cmd.none {model with WX = newWX}, Cmd.none
| DeleteWires (connectionIds : ConnectionId list) -> | DeleteWires (connectionIds : ConnectionId list) ->
let newModel = resetWireSegmentJumps (connectionIds) (model)
let newWX = let newWX =
newModel.WX model.WX
|> Map.filter (fun id _wire -> not (List.contains id connectionIds)) |> Map.filter (fun id _ -> not (List.contains id connectionIds))
{newModel with WX = newWX}, Cmd.ofMsg BusWidths {model with WX = newWX}, Cmd.ofMsg BusWidths
| DragWire (connId : ConnectionId, mMsg: MouseT) -> | DragWire (connId : ConnectionId, mMsg: MouseT) ->
match mMsg.Op with match mMsg.Op with
@ -1714,20 +1680,23 @@ let update (msg : Msg) (model : Model) : Model*Cmd<Msg> =
let segId = getClickedSegment model connId mMsg.Pos let segId = getClickedSegment model connId mMsg.Pos
{model with SelectedSegment = segId }, Cmd.none {model with SelectedSegment = segId }, Cmd.none
| Drag -> | Drag ->
let segId = model.SelectedSegment let aSeg =
let rec getSeg (segList: ASeg list) = let aSegOption =
match segList with riSegWireToASegs model.WX[connId]
| h::t -> if h.Id = segId then h else getSeg t |> List.choose ( fun aSeg -> if aSeg.Id = model.SelectedSegment then Some aSeg else None )
| _ -> failwithf "segment Id not found in segment list" |> List.tryExactlyOne
let seg = getSeg model.WX[connId].Segments match aSegOption with
if seg.Draggable then | Some aSeg -> aSeg
let distanceToMove = | None -> failwithf "Error: Segment Id not found in segment list"
match seg.Dir with
| Horizontal -> mMsg.Pos.Y - abs seg.Start.Y
| Vertical -> mMsg.Pos.X - abs seg.Start.X
let newWire = moveSegment seg distanceToMove model if aSeg.Draggable then
let newWX = Map.add seg.HostId newWire model.WX let distanceToMove =
match aSeg.Dir with
| Horizontal -> mMsg.Pos.Y - abs aSeg.Start.Y
| Vertical -> mMsg.Pos.X - abs aSeg.Start.X
let newWire = moveSegment aSeg distanceToMove model
let newWX = Map.add aSeg.HostId newWire model.WX
{model with WX = newWX}, Cmd.none {model with WX = newWX}, Cmd.none
else else
@ -1735,7 +1704,6 @@ let update (msg : Msg) (model : Model) : Model*Cmd<Msg> =
| _ -> model, Cmd.none | _ -> model, Cmd.none
| ColorWires (connIds, color) -> // Just Changes the colour of the wires, Sheet calls pasteWires before this | ColorWires (connIds, color) -> // Just Changes the colour of the wires, Sheet calls pasteWires before this
let newWires = let newWires =
(List.fold (fun prevWires cId -> (List.fold (fun prevWires cId ->
@ -1748,46 +1716,26 @@ let update (msg : Msg) (model : Model) : Model*Cmd<Msg> =
Map.add cId { oldWire with Color = color } prevWires) model.WX connIds) Map.add cId { oldWire with Color = color } prevWires) model.WX connIds)
{ model with WX = newWires }, Cmd.none { model with WX = newWires }, Cmd.none
| ResetJumps connIds ->
printfn $"resetting jumps on {connIds.Length} wires"
let newModel =
model
|> resetWireSegmentJumps connIds
newModel, Cmd.none
| MakeJumps connIds ->
printfn $"making jumps on {connIds.Length} wires"
let newModel =
model
|> updateWireSegmentJumps connIds
newModel, Cmd.none
| ResetModel -> { model with WX = Map.empty; ErrorWires = []; Notifications = None }, Cmd.none | ResetModel -> { model with WX = Map.empty; ErrorWires = []; Notifications = None }, Cmd.none
| LoadConnections conns -> // we assume components (and hence ports) are loaded before connections | LoadConnections conns -> // we assume components (and hence ports) are loaded before connections
let posMatchesVertex (pos:XYPos) (vertex: float*float) = let posMatchesVertex (pos:XYPos) (vertex: float*float) =
let epsilon = 0.00001 let epsilon = 0.00001
abs (abs pos.X - abs (fst vertex)) < epsilon && abs (abs pos.X - abs (fst vertex)) < epsilon && abs (abs pos.Y - abs (snd vertex)) < epsilon
abs (abs pos.Y - abs (snd vertex)) < epsilon
|> (fun b -> if not b then printf $"Bad wire endpoint match on {pos} {vertex}"; b else b) |> (fun b -> if not b then printf $"Bad wire endpoint match on {pos} {vertex}"; b else b)
let newWX = let newWX =
conns let connsToWX conn =
|> List.map ( fun conn ->
let inputId = InputPortId conn.Target.Id let inputId = InputPortId conn.Target.Id
let outputId = OutputPortId conn.Source.Id let outputId = OutputPortId conn.Source.Id
let connId = ConnectionId conn.Id let connId = ConnectionId conn.Id
let segments = issieVerticesToASegs connId conn.Vertices let aSegs = issieVerticesToASegs connId conn.Vertices
let riSegs = aSegListToRISegList aSegs
let makeWirePosMatchSymbol inOut (wire:Wire) = let makeWirePosMatchSymbol inOut (wire:Wire) =
match inOut with match inOut with
| true -> posMatchesVertex | true -> posMatchesVertex
(Symbol.getInputPortLocation model.Symbol inputId) (Symbol.getInputPortLocation model.Symbol inputId)
(List.head conn.Vertices) (List.head conn.Vertices)
| false -> | false -> posMatchesVertex
posMatchesVertex
(Symbol.getOutputPortLocation model.Symbol outputId) (Symbol.getOutputPortLocation model.Symbol outputId)
(List.last conn.Vertices) (List.last conn.Vertices)
|> (fun b -> |> (fun b ->
@ -1800,35 +1748,34 @@ let update (msg : Msg) (model : Model) : Model*Cmd<Msg> =
|> Option.bind (fun symId -> Map.tryFind (ComponentId symId) model.Symbol.Symbols) |> Option.bind (fun symId -> Map.tryFind (ComponentId symId) model.Symbol.Symbols)
|> Option.map (fun sym -> sym.Compo.Label) |> Option.map (fun sym -> sym.Compo.Label)
printfn $"Updating loaded wire from {getS conn.Source.Id}->{getS conn.Target.Id} of wire " printfn $"Updating loaded wire from {getS conn.Source.Id}->{getS conn.Target.Id} of wire "
updateWire model wire inOut) updateWire model wire inOut
)
connId, connId,
{ { Id = ConnectionId conn.Id
Id = ConnectionId conn.Id
InputPort = inputId InputPort = inputId
OutputPort = outputId OutputPort = outputId
Color = HighLightColor.DarkSlateGrey Color = HighLightColor.DarkSlateGrey
Width = 1 Width = 1
Segments = segments Rotation = PosY
YReflect = false
Segments = List.map riSegToASeg riSegs
} }
|> makeWirePosMatchSymbol false |> makeWirePosMatchSymbol false
|> makeWirePosMatchSymbol true |> makeWirePosMatchSymbol true
)
conns
|> List.map connsToWX
|> Map.ofList |> Map.ofList
let connIds = { model with WX = newWX }, Cmd.none
conns
|> List.map (fun conn -> ConnectionId conn.Id)
{ model with WX = newWX }, Cmd.ofMsg (MakeJumps connIds)
//---------------Other interface functions--------------------// //---------------Other interface functions--------------------//
///
let wireIntersectsBoundingBox (w : Wire) (bb : BoundingBox) = let wireIntersectsBoundingBox (w : Wire) (bb : BoundingBox) =
let boolList = List.map (fun seg -> fst(segmentIntersectsBoundingBoxCoordinates seg bb)) w.Segments w.Segments
List.contains true boolList |> List.map ( fun seg -> fst (segmentIntersectsBoundingBoxCoordinates seg bb) )
|> List.contains true
/// ///
let getIntersectingWires (wModel : Model) (selectBox : BoundingBox) : ConnectionId list = let getIntersectingWires (wModel : Model) (selectBox : BoundingBox) : ConnectionId list =
@ -1884,7 +1831,7 @@ let pasteWires (wModel : Model) (newCompIds : ComponentId list) : (Model * Conne
{ wModel with WX = newWireMap }, pastedConnIds { wModel with WX = newWireMap }, pastedConnIds
/// ///Given the Model and the connectionID list, return the tuple of InputPortId list and OutputPortId list
let getPortIdsOfWires (model: Model) (connIds: ConnectionId list) : (InputPortId list * OutputPortId list) = let getPortIdsOfWires (model: Model) (connIds: ConnectionId list) : (InputPortId list * OutputPortId list) =
(([], []), connIds) (([], []), connIds)
||> List.fold (fun (inputPorts, outputPorts) connId -> ||> List.fold (fun (inputPorts, outputPorts) connId ->

View file

@ -649,7 +649,6 @@ let mDownUpdate (model: Model) (mMsg: MouseT) : Model * Cmd<Msg> =
Cmd.batch [ symbolCmd (Symbol.SelectSymbols []) Cmd.batch [ symbolCmd (Symbol.SelectSymbols [])
wireCmd (BusWire.SelectWires [ connId ]) wireCmd (BusWire.SelectWires [ connId ])
wireCmd (BusWire.DragWire (connId, mMsg)) wireCmd (BusWire.DragWire (connId, mMsg))
wireCmd (BusWire.ResetJumps [ connId ] )
Cmd.ofMsg msg] Cmd.ofMsg msg]
| Canvas -> | Canvas ->
let newComponents, newWires = let newComponents, newWires =
@ -677,7 +676,7 @@ let mDragUpdate (model: Model) (mMsg: MouseT) : Model * Cmd<Msg> =
| InitialiseMoving _ -> | InitialiseMoving _ ->
let movingWires = BusWire.getConnectedWires model.Wire model.SelectedComponents let movingWires = BusWire.getConnectedWires model.Wire model.SelectedComponents
let newModel, cmd = moveSymbols model mMsg let newModel, cmd = moveSymbols model mMsg
newModel, Cmd.batch [ cmd; wireCmd (BusWire.ResetJumps movingWires) ] newModel, cmd
| MovingSymbols | DragAndDrop -> | MovingSymbols | DragAndDrop ->
moveSymbols model mMsg moveSymbols model mMsg
| ConnectingInput _ -> | ConnectingInput _ ->
@ -724,8 +723,7 @@ let mUpUpdate (model: Model) (mMsg: MouseT) : Model * Cmd<Msg> = // mMsg is curr
match model.Action with match model.Action with
| MovingWire connId -> | MovingWire connId ->
{ model with Action = Idle ; UndoList = appendUndoList model.UndoList newModel; RedoList = [] }, { model with Action = Idle ; UndoList = appendUndoList model.UndoList newModel; RedoList = [] },
Cmd.batch [ wireCmd (BusWire.DragWire (connId, mMsg)) wireCmd (BusWire.DragWire (connId, mMsg))
wireCmd (BusWire.MakeJumps [ connId ] ) ]
| Selecting -> | Selecting ->
let newComponents = findIntersectingComponents model model.DragToSelectBox let newComponents = findIntersectingComponents model model.DragToSelectBox
let newWires = BusWire.getIntersectingWires model.Wire model.DragToSelectBox let newWires = BusWire.getIntersectingWires model.Wire model.DragToSelectBox
@ -748,7 +746,6 @@ let mUpUpdate (model: Model) (mMsg: MouseT) : Model * Cmd<Msg> = // mMsg is curr
// Reset Movement State in Model // Reset Movement State in Model
match model.ErrorComponents with match model.ErrorComponents with
| [] -> | [] ->
let movingWires = BusWire.getConnectedWires model.Wire model.SelectedComponents
{model with {model with
// BoundingBoxes = Symbol.getBoundingBoxes model.Wire.Symbol // BoundingBoxes = Symbol.getBoundingBoxes model.Wire.Symbol
Action = Idle Action = Idle
@ -757,7 +754,7 @@ let mUpUpdate (model: Model) (mMsg: MouseT) : Model * Cmd<Msg> = // mMsg is curr
UndoList = appendUndoList model.UndoList newModel UndoList = appendUndoList model.UndoList newModel
RedoList = [] RedoList = []
AutomaticScrolling = false }, AutomaticScrolling = false },
wireCmd (BusWire.MakeJumps movingWires) Cmd.none
| _ -> | _ ->
let movingWires = BusWire.getConnectedWires model.Wire model.SelectedComponents let movingWires = BusWire.getConnectedWires model.Wire model.SelectedComponents
{model with {model with
@ -768,8 +765,7 @@ let mUpUpdate (model: Model) (mMsg: MouseT) : Model * Cmd<Msg> = // mMsg is curr
AutomaticScrolling = false }, AutomaticScrolling = false },
Cmd.batch [ symbolCmd (Symbol.MoveSymbols (model.SelectedComponents, (posDiff model.LastValidPos mMsg.Pos))) Cmd.batch [ symbolCmd (Symbol.MoveSymbols (model.SelectedComponents, (posDiff model.LastValidPos mMsg.Pos)))
symbolCmd (Symbol.SelectSymbols (model.SelectedComponents)) symbolCmd (Symbol.SelectSymbols (model.SelectedComponents))
wireCmd (BusWire.UpdateWires (model.SelectedComponents, posDiff model.LastValidPos mMsg.Pos)) wireCmd (BusWire.UpdateWires (model.SelectedComponents, posDiff model.LastValidPos mMsg.Pos)) ]
wireCmd (BusWire.MakeJumps movingWires) ]
| ConnectingInput inputPortId -> | ConnectingInput inputPortId ->
let cmd, undoList ,redoList = let cmd, undoList ,redoList =
if model.TargetPortId <> "" // If a target has been found, connect a wire if model.TargetPortId <> "" // If a target has been found, connect a wire