Add Rounded Corners Wire Rendering Implementation

This commit is contained in:
Aadi Desai 2022-03-14 13:14:19 +00:00
parent 7bbed7cde7
commit 8cf10dfd9e
No known key found for this signature in database
GPG key ID: CFFFE425830EF4D9

View file

@ -118,7 +118,9 @@ type Wire =
Segments: ASeg list Segments: ASeg list
} }
with static member stickLength = 16.0 with
static member stickLength = 16.0
static member arcSize = 8.0
/// ///
type Model = type Model =
@ -438,131 +440,82 @@ let makeInitialRISegList (hostId: ConnectionId) (portCoords: XYPos * XYPos) : RI
|> List.map aSegToRISeg |> List.map aSegToRISeg
// TODO: native RISeg implementation // TODO: native RISeg implementation
/// Render given segment using colour and width properties given /// Render given Rotation Invariant Segment using colour and width properties given
let renderSegment (segment: ASeg) (colour: string) (width: string) : ReactElement = /// Takes in 2 connecting segments to add rounded corners if appropriate
let wOpt = EEExtensions.String.tryParseWith System.Int32.TryParse width let renderRISegAndCorner (colour: string) (width: string) (segments: RISeg * RISeg * RISeg) : ReactElement =
let renderWidth = let prevSeg, segment', nextSeg = segments
match wOpt with let segment = {segment' with Start = absXYPos segment'.Start}
| Some 1 -> 1.5 let widthOption = EEExtensions.String.tryParseWith System.Int32.TryParse width
| Some n when n < int "8" -> 2.5 let renderWidth, halfWidth =
| _ -> 3.5 match widthOption with
let halfWidth = (renderWidth/2.0) - (0.75) | Some 1 -> 1.5, 0.25
| Some n when n < int "8" -> 2.5, 0.5
| _ -> 3.5, 1.0
let lineParameters = { defaultLine with Stroke = colour; StrokeWidth = string renderWidth } let lineParameters = { defaultLine with Stroke = colour; StrokeWidth = string renderWidth }
let circleParameters = { defaultCircle with R = halfWidth; Stroke = colour; Fill = colour } let circleParameters = { defaultCircle with R = halfWidth; Stroke = colour; Fill = colour }
if segment.Dir = Horizontal then
let pathParameters = { defaultPath with Stroke = colour; StrokeWidth = string renderWidth } let pathParameters = { defaultPath with Stroke = colour; StrokeWidth = string renderWidth }
let renderWireSubSegment (vertex1 : XYPos) (vertex2 : XYPos) : ReactElement list = // If first segment / previous or current segment too short / collinear with previous segment -> no corner
let Xa, Ya, Xb, Yb = vertex1.X, vertex1.Y, vertex2.X, vertex2.Y let startCorner = not (prevSeg.Index = -1 || (min (abs prevSeg.Length) (abs segment.Length)) < (Wire.arcSize * 2.0) || prevSeg.Dir = segment.Dir)
makeLine Xa Ya Xb Yb lineParameters
:: let endCorner =
makeCircle Xa Ya circleParameters // If last segment / current or next segment too short / collinear with next segment -> no corner
:: if nextSeg.Index = 0 || (min (abs segment.Length) (abs nextSeg.Length)) < (Wire.arcSize * 2.0) || segment.Dir = nextSeg.Dir then
let endPoint = getRISegEnd segment.Start segment
[ [
makeCircle Xb Yb circleParameters makeCircle endPoint.X endPoint.Y circleParameters
] ], false
else // Corner
let segmentJumpHorizontalSize = 9.0 let arcStart =
let segmentJumpVerticalSize = 6.0 match segment.Length with
| l when l < 0 -> getRISegEnd segment.Start {segment with Length = segment.Length + Wire.arcSize}
let renderSingleSegmentJump (intersectionCoordinate : XYPos) : ReactElement list = | _ -> getRISegEnd segment.Start {segment with Length = segment.Length - Wire.arcSize}
let x, y = intersectionCoordinate.X, intersectionCoordinate.Y let arcMid = nextSeg.Start
let arcEnd =
let startingPoint = {X = x - segmentJumpHorizontalSize/2.0; Y = y} match nextSeg.Length with
let startingControlPoint = {X = x - segmentJumpHorizontalSize/2.0; Y = y - segmentJumpVerticalSize} | l when l < 0 -> getRISegEnd nextSeg.Start {nextSeg with Length = - Wire.arcSize}
let endingControlPoint = {X = x + segmentJumpHorizontalSize/2.0; Y = y - segmentJumpVerticalSize} | _ -> getRISegEnd nextSeg.Start {nextSeg with Length = Wire.arcSize}
let endingPoint = {X = x + segmentJumpHorizontalSize/2.0; Y = y}
makePath startingPoint startingControlPoint endingControlPoint endingPoint pathParameters
::
makeCircle startingPoint.X startingPoint.Y circleParameters
::
[ [
makeCircle endingPoint.X endingPoint.Y circleParameters makeCircle arcStart.X arcStart.Y circleParameters
] makePath arcStart arcMid arcEnd arcEnd pathParameters
makeCircle arcEnd.X arcEnd.Y circleParameters
], true
let rec renderMultipleSegmentJumps (segmentJumpCoordinateList : float list) (segmentJumpYCoordinate : float) : ReactElement list =
match segmentJumpCoordinateList with
| [] -> []
| [singleElement] ->
renderSingleSegmentJump {X = singleElement; Y = segmentJumpYCoordinate}
| firstElement :: secondElement :: tailList ->
if (segment.Start.X > segment.End.X) then
renderSingleSegmentJump {X = firstElement; Y = segmentJumpYCoordinate}
@
renderWireSubSegment {X = firstElement - segmentJumpHorizontalSize/2.0; Y = segmentJumpYCoordinate} {X = secondElement + segmentJumpHorizontalSize/2.0; Y = segmentJumpYCoordinate}
@
renderMultipleSegmentJumps (secondElement :: tailList) (segmentJumpYCoordinate)
else
renderSingleSegmentJump {X = firstElement; Y = segmentJumpYCoordinate}
@
renderWireSubSegment {X = firstElement + segmentJumpHorizontalSize/2.0; Y = segmentJumpYCoordinate} {X = secondElement - segmentJumpHorizontalSize/2.0; Y = segmentJumpYCoordinate}
@
renderMultipleSegmentJumps (secondElement :: tailList) (segmentJumpYCoordinate)
let completeWireSegmentRenderFunction (seg : ASeg) : ReactElement list =
let jumpCoordinateList =
if (segment.Start.X > segment.End.X) then
seg.JumpCoordinateListA
|> List.map fst
|> List.sortDescending
else
seg.JumpCoordinateListA
|> List.map fst
|> List.sort
match jumpCoordinateList with
| [] -> renderWireSubSegment seg.Start seg.End
| lst ->
let y = seg.Start.Y // SHOULD be equal to seg.End.Y since ONLY horizontal segments have jumps
let firstSegmentJumpCoordinate = lst[0]
let lastSegmentJumpCoordinate = lst[(List.length lst) - 1]
if (segment.Start.X > segment.End.X) then
renderWireSubSegment seg.Start {X = firstSegmentJumpCoordinate + segmentJumpHorizontalSize/2.0; Y = y}
@
renderMultipleSegmentJumps lst y
@
renderWireSubSegment {X = lastSegmentJumpCoordinate - segmentJumpHorizontalSize/2.0; Y = y} seg.End
else
renderWireSubSegment seg.Start {X = firstSegmentJumpCoordinate - segmentJumpHorizontalSize/2.0; Y = y}
@
renderMultipleSegmentJumps lst y
@
renderWireSubSegment {X = lastSegmentJumpCoordinate + segmentJumpHorizontalSize/2.0; Y = y} seg.End
let wireSegmentReactElementList = segment
|> completeWireSegmentRenderFunction
g [] wireSegmentReactElementList
else
let Xa, Ya, Xb, Yb = segment.Start.X, segment.Start.Y, segment.End.X, segment.End.Y
let segmentElements = let segmentElements =
makeLine Xa Ya Xb Yb lineParameters let segStart =
:: match startCorner, segment.Length with
makeCircle Xa Ya circleParameters | true, l when l < 0 -> getRISegEnd segment.Start {segment with Length = - Wire.arcSize}
:: | true, _ -> getRISegEnd segment.Start {segment with Length = Wire.arcSize}
| false, _ -> segment.Start
let segEnd =
match (snd endCorner), segment.Length with
| true, l when l < 0 -> getRISegEnd segment.Start {segment with Length = segment.Length + Wire.arcSize}
| true, _ -> getRISegEnd segment.Start {segment with Length = segment.Length - Wire.arcSize}
| false, _ -> getRISegEnd segment.Start segment
[ [
makeCircle Xb Yb circleParameters makeLine segStart.X segStart.Y segEnd.X segEnd.Y lineParameters
] ]
@
fst endCorner
g [] segmentElements g [] segmentElements
/// 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 riSegs =
segs
|> List.filter ( fun seg -> seg.Length <> 0.0 )
|> List.mapi (fun i seg -> {seg with Index = i})
|> List.toSeq
let dummyStartRISeg = {Seq.head riSegs with Index = -1; Length = 0.0}
let dummyEndRISeg = {Seq.head riSegs with Index = 0; Length = 0.0}
let riSegTriplets =
Seq.zip3 (Seq.append [dummyStartRISeg] riSegs) riSegs (Seq.append (Seq.tail riSegs) [dummyEndRISeg])
|> List.ofSeq
riSegTriplets
|> List.map ( fun segTrip -> renderRISegAndCorner colour width segTrip )
/// ///
type WireRenderProps = type WireRenderProps =
{ {
@ -578,8 +531,8 @@ let singleWireView =
fun (props: WireRenderProps) -> fun (props: WireRenderProps) ->
let renderWireSegmentList : ReactElement list = let renderWireSegmentList : ReactElement list =
props.Segments props.Segments
|> List.map ( fun (segment: ASeg) -> renderSegment segment (props.ColorP.Text()) (string props.StrokeWidthP) ) |> List.map aSegToRISeg
// Call render helper functions on each segment, including jump rendering |> renderRISegList (props.ColorP.Text()) (string props.StrokeWidthP)
let renderWireWidthText : ReactElement = let renderWireWidthText : ReactElement =
let textParameters = let textParameters =