All Downloads are FREE. Search and download functionalities are using the official Maven repository.

doodle.algebra.generic.Finalized.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2015 Creative Scala
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package doodle
package algebra
package generic

import cats.Eval
import cats.Later
import cats.data.*
import doodle.core.BoundingBox
import doodle.core.Transform as Tx

/** A [[Finalized]] represents an effect that, when run, produces all the
  * information needed to layout an image (it "finalizes" all the information
  * needed for this process) and can eventually produce a value of type `A`
  * (once it is rendered). Algorithmically this means:
  *
  *   - for each shape work out its [[DrawingContext]] from which we can work
  *     out a [[doodle.core.BoundingBox]].
  *
  *   - apply transforms to bounding boxes at the point they are defined so each
  *     transformed subtree is laid out in its local coordinate system.
  *
  * The List of ContextTransform's are supplied in the order they should be
  * applied: the innermost transform is at the head of the list.
  */
final case class Finalized[F[_], A](
    f: Transforms => Eval[(BoundingBox, Renderable[F, A])]
) {
  def boundingBox: BoundingBox = {
    val (bb, _) = run(List.empty).value
    bb
  }

  def map[B](
      f: (BoundingBox, Renderable[F, A]) => (BoundingBox, Renderable[F, B])
  ): Finalized[F, B] =
    Finalized(ctxTxs => run(ctxTxs).map((bb, rdr) => f(bb, rdr)))

  def flatMap[B](
      f: (BoundingBox, Renderable[F, A]) => Finalized[F, B]
  ): Finalized[F, B] =
    Finalized(ctxTxs =>
      run(ctxTxs).flatMap((bb, rdr) => f(bb, rdr).run(ctxTxs))
    )

  def run(
      ctxTxs: List[ContextTransform]
  ): Eval[(BoundingBox, Renderable[F, A])] =
    f(ctxTxs)
}
object Finalized {
  def applyU[F[_], A](
      f: Transforms => (BoundingBox, Renderable[F, A])
  ): Finalized[F, A] =
    Finalized(ctxTxs => Later(f(ctxTxs)))

  /** Create a leaf [[Finalized]]. It will be passed a [[DrawingContext]] with
    * all transforms applied in the correct order.
    */
  def leaf[F[_], A](
      f: DrawingContext => (BoundingBox, Renderable[F, A])
  ): Finalized[F, A] =
    Finalized.applyU { ctxTxs =>
      val dc = ctxTxs.foldLeft(DrawingContext.default)((dc, f) => f(dc))
      f(dc)
    }

  def contextTransform[F[_], A](
      f: DrawingContext => DrawingContext
  )(child: Finalized[F, A]): Finalized[F, A] =
    Finalized(ctxTxs => child.run(f :: ctxTxs))

  def transform[F[_], A](transform: Tx)(
      child: Finalized[F, A]
  ): Finalized[F, A] =
    Finalized { ctxTxs =>
      child
        .run(ctxTxs)
        .map((bb, rdr) =>
          (bb.transform(transform), rdr.contramap(tx => transform.andThen(tx)))
        )
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy