Browser-BIM (cad): semantisches Modell, abgeleitete 2D/3D-Sichten, Zeichenwerkzeuge
Standalone-Browser-Port von DOSSIER. Enthaelt das semantische Modell mit Plan-/3D-Ableitung, Zeichen- und Editierwerkzeuge, Rhino-artiges Befehlssystem, dockbares Panel-System, Resource-Manager, DXF/.lin/.pat-Import, i18n (de/en) sowie Projektdokumentation und Probe-Harness.
This commit is contained in:
@@ -0,0 +1,469 @@
|
||||
// Das semantische Gebäudemodell — der Kern von BIM.
|
||||
// Bauteile haben Bedeutung, nicht nur Form. Eine Tür "kennt" ihre Wand,
|
||||
// eine Wand "kennt" ihren mehrschichtigen Aufbau (WallType).
|
||||
//
|
||||
// Dokumentmodell (nach DOSSIER): zwei UNABHÄNGIGE Achsen.
|
||||
// 1. Zeichnungsebenen (DrawingLevel) = oberste Schnitte: Geschosse +
|
||||
// Schnitte/Ansichten.
|
||||
// 2. Ebenen (LayerCategory) = Grafik-Kategorie-Schema, das für jedes
|
||||
// Geschoss gilt. Ein Element lebt auf einer Kategorie (code) UND einem
|
||||
// Geschoss.
|
||||
|
||||
export type Vec2 = { x: number; y: number };
|
||||
|
||||
// ── Ressourcen-Bibliotheken (Vectorworks-/DOSSIER-Stil) ────────────────────
|
||||
// Verwaltete, per id verwiesene Stil-Ressourcen. Verweis-Kette:
|
||||
// Component → Hatch → LineStyle.
|
||||
// Darstellung wird beim Rendern aus diesen Ressourcen aufgelöst, nie in die
|
||||
// Geometrie eingebacken (siehe docs/design/resources-graphics.md).
|
||||
|
||||
/** Ein wiederverwendbarer Linienstil (Line Manager). */
|
||||
export interface LineStyle {
|
||||
id: string;
|
||||
name: string;
|
||||
/** Strichstärke in Millimetern (≙ Rhino PlotWeight). */
|
||||
weight: number;
|
||||
/** Farbe (hex). */
|
||||
color: string;
|
||||
/** Strichmuster in Millimetern; `null` = durchgezogen. */
|
||||
dash: number[] | null;
|
||||
}
|
||||
|
||||
/** Mögliche Schraffur-Muster im Plan/Schnitt. */
|
||||
export type HatchPattern =
|
||||
| "none"
|
||||
| "solid"
|
||||
| "insulation"
|
||||
| "diagonal"
|
||||
| "crosshatch";
|
||||
|
||||
/** Eine wiederverwendbare Schraffur (Hatch Manager). */
|
||||
export interface HatchStyle {
|
||||
id: string;
|
||||
name: string;
|
||||
/** Muster-Typ. */
|
||||
pattern: HatchPattern;
|
||||
/** Grundmaßstab des Musters (1 = Standardteilung). */
|
||||
scale: number;
|
||||
/** Drehung des Musters in Grad. */
|
||||
angle: number;
|
||||
/**
|
||||
* Farbe der Musterlinien bzw. der Vollfüllung (`pattern==="solid"`). Bei
|
||||
* `pattern==="none"` ungenutzt.
|
||||
*/
|
||||
color: string;
|
||||
/** Optionaler Linienstil für die Musterlinien (Line Manager). */
|
||||
lineStyleId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ein Bauteil-Material (Component Manager) — vereint Plan-Schnittdarstellung
|
||||
* (Schraffur) und 3D-Erscheinung. Ersetzt das frühere `Material`.
|
||||
*/
|
||||
export interface Component {
|
||||
id: string;
|
||||
name: string;
|
||||
/** Füllfarbe im Grundriss (Poché) und 3D-Diffusfarbe. */
|
||||
color: string;
|
||||
/** Schnitt-Schraffur → Hatch Manager. */
|
||||
hatchId: string;
|
||||
/** Optionale 3D-Textur (vorerst ignoriert). */
|
||||
texture3d?: string;
|
||||
/** Verschneidungs-Rang: höher läuft am Stoß durch (Backbone). */
|
||||
joinPriority: number;
|
||||
}
|
||||
|
||||
/** Eine Schicht eines mehrschichtigen Bauteils. */
|
||||
export interface Layer {
|
||||
/** Verweis auf das Bauteil-Material (Component Manager). */
|
||||
componentId: string;
|
||||
/** Schichtdicke in Metern. */
|
||||
thickness: number;
|
||||
}
|
||||
|
||||
/** Ein Wandtyp = geordneter Schichtaufbau (außen → innen). */
|
||||
export interface WallType {
|
||||
id: string;
|
||||
name: string;
|
||||
layers: Layer[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Art einer Zeichnungsebene.
|
||||
* • "floor" — Geschoss, trägt Bauteile.
|
||||
* • "section" — Schnitt (abgeleitete Projektion entlang einer Linie).
|
||||
* • "elevation" — Ansicht (abgeleitete Projektion entlang einer Linie).
|
||||
* • "drawing" — freie 2D-Zeichnung, nicht an ein Geschoss gebunden.
|
||||
*/
|
||||
export type DrawingLevelKind = "floor" | "section" | "elevation" | "drawing";
|
||||
|
||||
/**
|
||||
* Eine Zeichnungsebene (DrawingLevel) — eine oberste Schnittebene des
|
||||
* Dokuments. Ein Geschoss trägt die Bauteile (über `floorId`); ein
|
||||
* Schnitt/Ansicht ist eine abgeleitete Projektion (vorerst Platzhalter); eine
|
||||
* reine Zeichnung ist eine freie 2D-Ebene ohne Geschossbezug.
|
||||
*
|
||||
* Geschoss nutzt floorHeight/cutHeight/baseElevation; Schnitt/Ansicht nutzen
|
||||
* linePoints/directionSign; "drawing" nutzt keines dieser Felder.
|
||||
*/
|
||||
export interface DrawingLevel {
|
||||
id: string;
|
||||
name: string;
|
||||
kind: DrawingLevelKind;
|
||||
/** Sichtbarkeit der Zeichnungsebene im Navigator. */
|
||||
visible: boolean;
|
||||
/** Gesperrt (keine Bearbeitung). */
|
||||
locked: boolean;
|
||||
/** Lichte Geschosshöhe in Metern (nur Geschoss). */
|
||||
floorHeight?: number;
|
||||
/** Schnitthöhe über OKFF in Metern (nur Geschoss, für den Grundriss). */
|
||||
cutHeight?: number;
|
||||
/** Oberkante Fertigfußboden in Metern (nur Geschoss). */
|
||||
baseElevation?: number;
|
||||
/** Schnitt-/Ansichtslinie im Grundriss (nur Schnitt/Ansicht). */
|
||||
linePoints?: [Vec2, Vec2];
|
||||
/** Blickrichtung relativ zur Schnittlinie (nur Schnitt/Ansicht). */
|
||||
directionSign?: 1 | -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eine Ebene (LayerCategory) — ein Knoten im Grafik-Kategorie-Baum. Das
|
||||
* Schema gilt geschossübergreifend; Elemente verweisen über `code` darauf.
|
||||
*/
|
||||
export interface LayerCategory {
|
||||
/** Eindeutiger Kategorie-Code, z. B. "20" für Wände. */
|
||||
code: string;
|
||||
name: string;
|
||||
/** Darstellungsfarbe (hex). */
|
||||
color: string;
|
||||
/** Linienstärke in Millimetern. */
|
||||
lw: number;
|
||||
visible: boolean;
|
||||
locked: boolean;
|
||||
/** Optionale Standard-Schraffur der Kategorie. */
|
||||
hatch?: string;
|
||||
/** Unterkategorien (Baum). */
|
||||
children?: LayerCategory[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Lage der Wandachse (Referenzlinie) über die Dicke der Wand — analog
|
||||
* Vectorworks. Gemessen relativ zur Laufrichtung (start→end) mit der
|
||||
* leftNormal-Konvention `n = (-u.y, u.x)`:
|
||||
* • "center" — Achse mittig (Default = heutiges Verhalten).
|
||||
* • "left" — Achse liegt auf der linken Wandfläche (+n-Seite, „außen").
|
||||
* • "right" — Achse liegt auf der rechten Wandfläche (−n-Seite, „innen").
|
||||
*/
|
||||
export type WallReferenceLine = "left" | "center" | "right";
|
||||
|
||||
/**
|
||||
* Vertikale Bindung einer Wandkante (UK/OK).
|
||||
* • "floor" — an ein Geschoss gebunden: Z = baseElevation des Geschosses
|
||||
* (+ optionalem `offset`). Stapelt automatisch mit dem Geschoss.
|
||||
* • "custom" — fester absoluter Z-Wert (Meter).
|
||||
*/
|
||||
export type VerticalAnchor =
|
||||
| { mode: "floor"; floorId: string; offset?: number }
|
||||
| { mode: "custom"; z: number };
|
||||
|
||||
/** Eine Wand, definiert über ihre Achse (Centerline) und ihren Typ. */
|
||||
export interface Wall {
|
||||
id: string;
|
||||
type: "wall";
|
||||
/** Zugehörige Zeichnungsebene (Geschoss). */
|
||||
floorId: string;
|
||||
/** Grafik-Kategorie (Ebene), z. B. "20" für Wände. */
|
||||
categoryCode: string;
|
||||
/** Achs-Startpunkt im Grundriss (Meter). */
|
||||
start: Vec2;
|
||||
/** Achs-Endpunkt im Grundriss (Meter). */
|
||||
end: Vec2;
|
||||
/** Verweis auf den (mehrschichtigen) Wandtyp. */
|
||||
wallTypeId: string;
|
||||
/** Wandhöhe in Metern. */
|
||||
height: number;
|
||||
/**
|
||||
* Optionale Übersteuerung der Strich-/Umrandungsfarbe der Wand; sonst gilt
|
||||
* die Kategorie-Farbe. Übersteuert NUR die Linienfarbe (Umriss/Schichtfugen),
|
||||
* nicht die Schicht-Füllfarben/Schraffuren.
|
||||
*/
|
||||
color?: string;
|
||||
/**
|
||||
* Lage der Wandachse über die Dicke. Fehlt sie, gilt "center" (= heutiges
|
||||
* Verhalten: Schichten symmetrisch −T/2 … +T/2 um die Achse).
|
||||
*/
|
||||
referenceLine?: WallReferenceLine;
|
||||
/**
|
||||
* Vertikale Bindung der Unterkante (UK). Fehlt sie, sitzt die UK auf der
|
||||
* baseElevation des zugehörigen Geschosses (= heutiges Verhalten).
|
||||
*/
|
||||
bottom?: VerticalAnchor;
|
||||
/**
|
||||
* Vertikale Bindung der Oberkante (OK). Fehlt sie, ergibt sich die OK aus
|
||||
* UK + `height` (= heutiges Verhalten). „custom" setzt einen absoluten
|
||||
* Z-Wert; „floor" bindet die OK an ein (z. B. nächsthöheres) Geschoss.
|
||||
*/
|
||||
top?: VerticalAnchor;
|
||||
}
|
||||
|
||||
/** Schwenkrichtung der Tür relativ zur Wandachse. */
|
||||
export type SwingSide = "left" | "right";
|
||||
|
||||
/** Eine Tür, gehostet in einer Wand. Ihr Geschoss ergibt sich aus der Wand. */
|
||||
export interface Door {
|
||||
id: string;
|
||||
type: "door";
|
||||
hostWallId: string;
|
||||
/** Grafik-Kategorie (Ebene), z. B. "21" für Türen/Fenster. */
|
||||
categoryCode: string;
|
||||
/** Abstand des Türanschlags (erster Pfosten) vom Wand-Startpunkt, in Metern. */
|
||||
position: number;
|
||||
/** Türbreite (lichte Öffnung) in Metern. */
|
||||
width: number;
|
||||
/** Türhöhe in Metern. */
|
||||
height: number;
|
||||
/** Auf welche Seite der Wandachse die Tür aufschlägt. */
|
||||
swing: SwingSide;
|
||||
/** An welchem Pfosten das Scharnier sitzt. */
|
||||
hinge: "start" | "end";
|
||||
}
|
||||
|
||||
// ── Freie 2D-Zeichengeometrie (Drawing2D) ──────────────────────────────────
|
||||
// Ein semantisches 2D-Element (wie Wall/Door), das beim Rendern abgeleitet wird
|
||||
// (keine vorab erzeugten Plan-Primitive). Siehe docs/design/drawing-tools.md §7.
|
||||
|
||||
/** Geometrie-Form eines 2D-Zeichenelements. */
|
||||
export type Drawing2DGeom =
|
||||
| { shape: "line"; a: Vec2; b: Vec2 }
|
||||
| { shape: "polyline"; pts: Vec2[]; closed: boolean }
|
||||
| { shape: "rect"; min: Vec2; max: Vec2 }
|
||||
| { shape: "circle"; center: Vec2; r: number }
|
||||
| { shape: "arc"; center: Vec2; r: number; a0: number; a1: number }
|
||||
| { shape: "text"; at: Vec2; text: string; height: number; angle: number };
|
||||
|
||||
/** Ein freies 2D-Zeichenelement auf einer Zeichnungsebene. */
|
||||
export interface Drawing2D {
|
||||
id: string;
|
||||
type: "drawing2d";
|
||||
/** Zeichnungsebene (Geschoss ODER freie 2D-Ebene). */
|
||||
levelId: string;
|
||||
/** Grafik-Kategorie (Ebene) — liefert Farbe/Strichstärke als Default. */
|
||||
categoryCode: string;
|
||||
geom: Drawing2DGeom;
|
||||
/** Optionaler Linienstil (Line Manager); sonst Kategorie-Default. */
|
||||
lineStyleId?: string;
|
||||
/** Optionale Schraffur für geschlossene Formen (Hatch Manager). */
|
||||
hatchId?: string;
|
||||
/** Optionale explizite Strichfarbe; sonst Kategorie-Farbe. */
|
||||
color?: string;
|
||||
/**
|
||||
* Optionale Vollton-Füllfarbe für geschlossene Formen (getrennt von der
|
||||
* Strichfarbe `color`). Fehlt sie, ist die Fläche transparent (nur Schraffur
|
||||
* bzw. ungefüllt).
|
||||
*/
|
||||
fillColor?: string;
|
||||
/**
|
||||
* Optionale direkte Strichstärke-Übersteuerung in Millimetern; hat Vorrang
|
||||
* vor dem LineStyle-Gewicht und der Kategorie-Strichstärke.
|
||||
*/
|
||||
weightMm?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ein Kanten-/Seiten-Griff eines selektierten Elements (zusätzlich zu den
|
||||
* Eckpunkt-Griffen). Liegt am Mittelpunkt einer Seite (Modell-Meter) und zeigt
|
||||
* mit `normal` als Einheitsvektor nach AUSSEN (vom Element weg). `aIndex`/
|
||||
* `bIndex` sind die beiden Vertex-Indizes der Kante — passend zur Indizierung
|
||||
* von `drawingVertices`/`moveGripOf`. Ziehen verschiebt BEIDE Vertices senkrecht
|
||||
* zur Kante (Form wächst/schrumpft an dieser Seite).
|
||||
*/
|
||||
export interface EdgeGrip {
|
||||
mid: Vec2;
|
||||
normal: Vec2;
|
||||
aIndex: number;
|
||||
bIndex: number;
|
||||
}
|
||||
|
||||
export type Element = Wall | Door | Drawing2D;
|
||||
|
||||
// ── Kontext-Geometrie (importiert / abgeleitet, NICHT semantisch) ───────────
|
||||
// Importierte Geometrie ist „dumme" KONTEXT-Geometrie (Anzeige + späteres
|
||||
// Snap-Ziel), KEIN Teil des semantischen BIM-Modells. Sie lebt in einer eigenen
|
||||
// Schicht `Project.context` und wird beim Rendern wie eine Referenz behandelt.
|
||||
// Bewusst three-frei: nur rohe Buffer-Daten (positions/indices), three-Objekte
|
||||
// entstehen erst im Viewport.
|
||||
|
||||
/**
|
||||
* Ein importiertes Dreiecks-Mesh (z. B. aus DXF 3DFACE/POLYFACE/MESH). Rohe
|
||||
* BufferGeometry-Daten: `positions` = flaches Array (x,y,z, x,y,z, …) in Metern,
|
||||
* `indices` = Dreiecks-Indizes (je 3 ein Dreieck). Keine three-Objekte.
|
||||
*/
|
||||
export interface ImportedMesh {
|
||||
id: string;
|
||||
type: "importedMesh";
|
||||
name: string;
|
||||
/** Ursprünglicher DXF-Layer-Name (für spätere Kategorisierung). */
|
||||
layer?: string;
|
||||
positions: number[];
|
||||
indices: number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Eine einzelne Kontur (Höhenlinie / Polylinie) auf konstanter Höhe `z`. `pts`
|
||||
* sind 2D-Stützpunkte (x,y) in Metern; `closed` schließt den Linienzug.
|
||||
*/
|
||||
export interface Contour {
|
||||
z: number;
|
||||
pts: Vec2[];
|
||||
closed: boolean;
|
||||
/** Ursprünglicher DXF-Layer-Name (für die Kategorisierung beim 2D-Import). */
|
||||
layer?: string;
|
||||
}
|
||||
|
||||
/** Ein Satz Konturen (z. B. alle Höhenlinien eines DXF-Imports). */
|
||||
export interface ContourSet {
|
||||
id: string;
|
||||
type: "contourSet";
|
||||
name: string;
|
||||
/** Ursprünglicher DXF-Layer-Name (für spätere Kategorisierung). */
|
||||
layer?: string;
|
||||
contours: Contour[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ein TIN-Geländemodell, abgeleitet aus Konturen (Delaunay über (x,y), Z aus der
|
||||
* jeweiligen Kontur-Höhe). `positions` = flaches (x,y,z…)-Array in Metern,
|
||||
* `indices` = Dreiecks-Indizes. Gelände ist NICHT semantisch (Kontext-Schicht).
|
||||
*/
|
||||
export interface TerrainMesh {
|
||||
id: string;
|
||||
type: "terrainMesh";
|
||||
name: string;
|
||||
positions: number[];
|
||||
indices: number[];
|
||||
}
|
||||
|
||||
/** Ein Kontext-Objekt: importiertes Mesh, Konturen-Satz oder abgeleitetes TIN. */
|
||||
export type ContextObject = ImportedMesh | ContourSet | TerrainMesh;
|
||||
|
||||
/** Das gesamte Projekt. */
|
||||
export interface Project {
|
||||
id: string;
|
||||
name: string;
|
||||
/** Linienstil-Bibliothek (Line Manager). */
|
||||
lineStyles: LineStyle[];
|
||||
/** Schraffur-Bibliothek (Hatch Manager). */
|
||||
hatches: HatchStyle[];
|
||||
/** Bauteil-Material-Bibliothek (Component Manager). */
|
||||
components: Component[];
|
||||
wallTypes: WallType[];
|
||||
/** Oberste Schnitte: Geschosse + Schnitte/Ansichten. */
|
||||
drawingLevels: DrawingLevel[];
|
||||
/** Grafik-Kategorie-Baum (geschossübergreifend). */
|
||||
layers: LayerCategory[];
|
||||
walls: Wall[];
|
||||
doors: Door[];
|
||||
/** Freie 2D-Zeichengeometrie (Line/Polyline/Rect/Circle/Arc/Text). */
|
||||
drawings2d: Drawing2D[];
|
||||
/**
|
||||
* Kontext-Schicht: importierte/abgeleitete „dumme" Geometrie (Meshes,
|
||||
* Konturen, Gelände-TIN) — NICHT semantisch. Optional, damit bestehende
|
||||
* Projekte/Tests ohne `context` gültig bleiben (Default: leer behandeln).
|
||||
*/
|
||||
context?: ContextObject[];
|
||||
}
|
||||
|
||||
// ── Helfer ───────────────────────────────────────────────────────────────
|
||||
|
||||
export const getWallType = (project: Project, wall: Wall): WallType => {
|
||||
const wt = project.wallTypes.find((t) => t.id === wall.wallTypeId);
|
||||
if (!wt) throw new Error(`Unbekannter Wandtyp: ${wall.wallTypeId}`);
|
||||
return wt;
|
||||
};
|
||||
|
||||
/** Liefert ein Bauteil-Material (Component) per ID oder wirft. */
|
||||
export const getComponent = (project: Project, id: string): Component => {
|
||||
const c = project.components.find((co) => co.id === id);
|
||||
if (!c) throw new Error(`Unbekanntes Bauteil-Material: ${id}`);
|
||||
return c;
|
||||
};
|
||||
|
||||
/** Liefert eine Schraffur (HatchStyle) per ID oder wirft. */
|
||||
export const getHatch = (project: Project, id: string): HatchStyle => {
|
||||
const h = project.hatches.find((ht) => ht.id === id);
|
||||
if (!h) throw new Error(`Unbekannte Schraffur: ${id}`);
|
||||
return h;
|
||||
};
|
||||
|
||||
/** Liefert einen Linienstil (LineStyle) per ID oder wirft. */
|
||||
export const getLineStyle = (project: Project, id: string): LineStyle => {
|
||||
const l = project.lineStyles.find((ls) => ls.id === id);
|
||||
if (!l) throw new Error(`Unbekannter Linienstil: ${id}`);
|
||||
return l;
|
||||
};
|
||||
|
||||
/** Liefert eine Zeichnungsebene (Geschoss) per ID oder wirft. */
|
||||
export const getFloor = (project: Project, id: string): DrawingLevel => {
|
||||
const g = project.drawingLevels.find((z) => z.id === id);
|
||||
if (!g) throw new Error(`Unbekanntes Geschoss: ${id}`);
|
||||
return g;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stapelt baseElevation der Geschosse in Dokumentreihenfolge: Das erste
|
||||
* Geschoss beginnt bei 0, jedes weitere bei baseElevation + floorHeight des
|
||||
* vorigen Geschosses. Nicht-Geschoss-Ebenen behalten baseElevation undefined.
|
||||
* Liefert eine neue Liste (mit neuen Geschoss-Objekten); die Eingabe bleibt
|
||||
* unverändert.
|
||||
*/
|
||||
export const recomputeFloorElevations = (
|
||||
levels: DrawingLevel[],
|
||||
): DrawingLevel[] => {
|
||||
let nextBase = 0;
|
||||
return levels.map((level) => {
|
||||
if (level.kind !== "floor") {
|
||||
return { ...level, baseElevation: undefined };
|
||||
}
|
||||
const baseElevation = nextBase;
|
||||
nextBase = baseElevation + (level.floorHeight ?? 0);
|
||||
return { ...level, baseElevation };
|
||||
});
|
||||
};
|
||||
|
||||
/** Flacht den Kategorie-Baum (Tiefensuche) in eine Liste ab. */
|
||||
export const flattenCategories = (cats: LayerCategory[]): LayerCategory[] => {
|
||||
const out: LayerCategory[] = [];
|
||||
const walk = (list: LayerCategory[]) => {
|
||||
for (const c of list) {
|
||||
out.push(c);
|
||||
if (c.children) walk(c.children);
|
||||
}
|
||||
};
|
||||
walk(cats);
|
||||
return out;
|
||||
};
|
||||
|
||||
/** Menge aller Codes sichtbarer Kategorien (Baum berücksichtigt). */
|
||||
export const collectVisibleCodes = (layers: LayerCategory[]): Set<string> => {
|
||||
const codes = new Set<string>();
|
||||
for (const c of flattenCategories(layers)) {
|
||||
if (c.visible) codes.add(c.code);
|
||||
}
|
||||
return codes;
|
||||
};
|
||||
|
||||
/** Gesamtdicke eines Wandtyps = Summe der Schichtdicken. */
|
||||
export const wallTypeThickness = (wt: WallType): number =>
|
||||
wt.layers.reduce((sum, l) => sum + l.thickness, 0);
|
||||
|
||||
/** Formatiert Meter mit zwei Nachkommastellen, z. B. "0.35 m". */
|
||||
export const formatM = (meters: number): string => meters.toFixed(2) + " m";
|
||||
|
||||
/**
|
||||
* Standard-Stiftstärken (mm Papier bei 100 %), Vorgabeliste für Linienstil-/
|
||||
* Strichstärke-Eingaben. Bedeutung: Breite auf dem Papier — die Linien skalieren
|
||||
* mit dem Massstab (non-scaling-stroke). Werte sind Vorschläge; man darf abweichen.
|
||||
*/
|
||||
export const PEN_WEIGHTS: number[] = [
|
||||
0.02, 0.1, 0.13, 0.18, 0.25, 0.35, 0.5, 0.7, 1.0, 1.4, 2.0,
|
||||
];
|
||||
Reference in New Issue
Block a user