% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/plot-mesh-polygon.R
\name{plot_mesh_polygon}
\alias{plot_mesh_polygon}
\title{Render one or more meshes as flat-shaded triangles in base R}
\usage{
plot_mesh_polygon(
  mesh,
  eye = c(0, 0, 1000),
  lookat = c(0, 0, 0),
  up = c(0, 1, 0),
  col = c("white", "gray30"),
  cex = 1,
  add = FALSE,
  axes = FALSE,
  asp = 1,
  xlim = NULL,
  ylim = NULL,
  zoom = 1,
  xlab = "",
  ylab = "",
  main = "",
  side = c("front", "back", "both"),
  mesh_clipping = 1,
  sphere_subdivision = 1L,
  alpha = 1,
  shadow_color = NULL,
  light_intensity = 1,
  ambient_intensity = 0.2,
  clipping_plane = NULL,
  clipping_plane_enabled = TRUE,
  ...
)
}
\arguments{
\item{mesh}{a \code{'mesh3d'} object, or a list of \code{'mesh3d'} objects.
Meshes without a face matrix (\code{$it}) are rendered as sphere
instances (one \code{\link{vcg_sphere}} per vertex, radius \code{cex}).}

\item{eye}{numeric vector of length 3 - camera position in world space.}

\item{lookat}{numeric vector of length 3 - the world-space point the camera
is looking at.}

\item{up}{numeric vector of length 3 - world-space "up" direction; defaults
to \code{c(0, 1, 0)}.}

\item{col}{base color(s) per mesh.  Same forms as
\code{\link{plot_mesh_dotcloud}}: a single color, a depth-gradient vector,
a per-vertex character vector, or a list of any of these (one element per
mesh).  Default \code{c("white", "gray30")}.}

\item{cex}{radius of the substitute sphere used for point-cloud meshes
(world units).  Has no effect on meshes that already have faces.  Default
\code{1}.}

\item{add}{logical; if \code{TRUE} the faces are added to an existing plot
instead of opening a new one.  Default \code{FALSE}.}

\item{axes, asp, xlim, ylim, xlab, ylab, main}{passed to
\code{\link[graphics]{plot.default}} when \code{add = FALSE}.}

\item{zoom}{positive numeric magnification applied to the auto-computed
axis limits when \code{xlim} or \code{ylim} is \code{NULL}.  Values
greater than \code{1} zoom in, values in \code{(0, 1)} zoom out.
When \code{asp} is set, the zoom is plot-region aware (queried via
\code{par("pin")}): the axis that already fills the device after
\code{asp}-induced expansion is zoomed by exactly \code{zoom}, and
the other axis is zoomed less so the data fills more of the plot
region while preserving the requested \code{asp}.  Ignored for any
axis whose limit was supplied explicitly, and when \code{add = TRUE}.
Default \code{1}.}

\item{side}{which side of each triangle to render.  One of \code{"front"}
(default), \code{"back"}, or \code{"both"} (shows all triangles).}

\item{mesh_clipping}{numeric in \code{[0, 1]} controlling camera-facing
clipping: any triangle whose signed Lambert dot product
(\eqn{n \cdot z_{cam}}, i.e. positive for front-facing) is at or
above \code{mesh_clipping} is discarded.  A value of \code{1}
(default) keeps the full surface; \code{0.5} peels a 60-degree cap
off the front and reveals the back wall (whose absolute Lambert
shade is still large, so it renders brightly); \code{0} clips
every front-facing triangle and shows the interior only.
Back-facing triangles are never clipped by this rule.  Default
\code{1}.}

\item{sphere_subdivision}{integer subdivision level forwarded to
\code{\link{vcg_sphere}} when substituting point clouds.  Higher values
give smoother spheres at the cost of more triangles.  Default \code{1}
(80 triangles per sphere).}

\item{alpha}{numeric in \code{[0, 1]}; one value per mesh (recycled),
sets the transparency of each mesh (\code{1} = fully opaque,
\code{0} = fully transparent).  Faces belonging to a mesh with
\code{alpha = 0} are dropped entirely.  Because faces are drawn in
back-to-front (painter's) order, alpha blending across multiple
meshes follows that order.  Default \code{1}.}

\item{shadow_color}{color used for fully unlit (grazing/back) faces.
The Lambert shade linearly interpolates from \code{shadow_color}
(at \code{shade = 0}) toward the face color lit by a white light
(at \code{shade = 1}), so unlit faces fade to this color instead
of an implicit black background.  Default \code{par("fg")}, so the
shadow contrasts with the current device's canvas.}

\item{light_intensity}{non-negative scalar controlling the brightness
of the (white) light source: at \code{shade = 1} the face color is
multiplied by \code{light_intensity} (clamped to \code{[0, 1]}).
\code{1} (default) reproduces the face color exactly when fully
lit; smaller values darken the whole mesh, larger values saturate.}

\item{ambient_intensity}{scalar in \code{[0, 1]} acting as a lower
bound on the Lambert shade so grazing/back faces still receive some
light and never collapse fully to \code{shadow_color}.  An effective
shade of \code{max(shade, ambient_intensity)} is used in the
background-to-light blend.  Default \code{0.2}.}

\item{clipping_plane}{optional list of world-space clipping planes used to
hide parts of the scene.  Each plane is a numeric vector of length 5:
the first three entries are the plane normal \eqn{(n_x, n_y, n_z)}
(must be non-zero; normalized internally), the fourth is the signed
distance from the world origin to the plane along that normal (so the
plane equation is \eqn{n \cdot x = d}), and the fifth indicates which
half-space is kept: \code{1} keeps the front side (the side the normal
points to), \code{-1} keeps the back side, and \code{0} keeps whichever
side currently faces the camera (auto-flipped per call based on
\code{eye}).  Multiple planes are intersected.  Clipping is applied
per face using the face centroid (a face is kept only when its
centroid lies on the kept side of every plane).  A single length-5
numeric vector is also accepted as shorthand for a one-plane list.
Default \code{NULL} (no clipping).}

\item{clipping_plane_enabled}{logical vector, one entry per mesh
(recycled), controlling whether \code{clipping_plane} is applied to
that mesh.  \code{TRUE} (default) means the mesh participates in
clipping; \code{FALSE} exempts the mesh entirely (all of its faces
are kept regardless of the clipping planes).  Has no effect when
\code{clipping_plane} is \code{NULL}.}

\item{...}{additional graphical parameters forwarded to
\code{\link[graphics]{plot.default}} (new plot only).}
}
\value{
Invisibly returns a list with components \code{xlim} and
  \code{ylim} (the plot limits used).
}
\description{
Projects each triangular face onto a 2D plane using an orthographic camera,
shades it with a single color proportional to how directly it faces the
camera (Lambert), depth-sorts all faces across all meshes, and draws them
in a single \code{\link[graphics]{polygon}} call.

Meshes without faces (point clouds) are substituted by a small sphere
(\code{\link{vcg_sphere}}) centered at each point and scaled by
\code{cex}; they then participate in the same rendering pipeline as ordinary
faced meshes.

A camera-facing clipping pass discards triangles whose outward normal
points along the camera ray (signed \eqn{n \cdot z_{cam}} \eqn{> 1 - \mathrm{mesh\_clipping}}),
peeling the front cap off the surface so the back wall (and any
interior meshes) become visible. Set \code{mesh_clipping = 1} to disable
clipping. Point-cloud meshes (those rendered as substitute
\code{\link{vcg_sphere}} instances) are exempt from this clip so they
remain solid even when the enclosing surface is peeled.

Multiple meshes share a single depth space: all faces are projected,
sorted, and drawn together so the painter's algorithm works correctly
across meshes.
}
\details{
Limitations of the base-R polygon path (no \code{rgl}):
\itemize{
  \item Flat shading only - one color per triangle.  No per-vertex
    color interpolation.
  \item No depth buffer - faces are depth-sorted by centroid (painter's
    algorithm).  Interpenetrating triangles can render in the wrong
    order.
  \item Anti-aliasing seams can appear between adjacent triangles on
    raster devices; Cairo-based devices (\code{png(type = "cairo")},
    \code{svg()}, \code{pdf()}) produce cleaner output than the default
    quartz/X11 path.
}
}
\section{Coercing Surface Inputs}{

The surface objects are converted to \code{'mesh3d'} object before
applying further calculations.

When \code{surface} is a surface \pkg{ieegio} object, the returned
\code{mesh3d$vb} contains vertices that have been left-multiplied by
\code{surface$geometry$transforms[[1]]} (the first transform stored in the
geometry, typically the \code{ScannerAnat} or voxel-to-world transform).

\strong{Breaking change:} Earlier versions (before 0.2.6) of \pkg{ravetools}
returned the raw \code{surface$geometry$vertices} without applying any
transform, so downstream code often multiplied by
\code{surface$geometry$transforms[[1]]} (or an equivalent) manually before
working in world space. Such code will now \emph{double}
apply the transform and produce incorrect coordinates. If you previously
applied a transform from \code{surface$geometry$transforms} by hand after
calling a \pkg{ravetools} mesh function on an \code{'ieegio_surface'},
remove that manual step.

Surfaces with an empty or missing \code{geometry$transforms} list (for
example, surfaces produced by \pkg{ieegio}'s \code{volume_to_surface},
which stores an identity transform) are unaffected.

If \code{geometry$transforms} contains multiple transforms targeting
different coordinate spaces, only the first one is used. Callers that need
a specific target space should select and apply that transform themselves
before calling \pkg{ravetools} mesh functions.
}

\examples{

mesh <- vcg_isosurface(left_hippocampus_mask)

# Surface alone
plot(
  mesh,
  eye    = c(150, 30, 0),
  lookat = c(0, 0, 0),
  up     = c(0, 0, 1),
  col    = "steelblue"
)

# Surface + electrode point cloud (rendered as small icospheres)
n_elec <- 20
electrodes <- structure(
  list(vb = matrix(rnorm(3 * n_elec, sd = 5), 3, n_elec) +
         rowMeans(mesh$vb)[1:3]),
  class = "mesh3d"
)
plot_mesh_polygon(
  mesh = list(mesh, electrodes),
  eye    = c(150, -30, 0),
  lookat = c(0, 0, 0),
  up     = c(0, 0, 1),
  alpha  = c(0.5, 1),
  col    = list("steelblue", "red"),
  cex    = 1.5
)

}
\seealso{
\code{\link{plot_mesh_dotcloud}}, \code{\link{vcg_isosurface}}
}
