Merge branch 'master' into python

pull/493/head
KmolYuan 2019-09-20 15:14:15 +08:00
commit 813217bf00
76 changed files with 2063 additions and 1160 deletions

View File

@ -10,22 +10,19 @@ ColumnLimit: 100
# Based on minimizing diff when applying clang-format
AccessModifierOffset: -4
DerivePointerAlignment: true
AlignConsecutiveAssignments: true
FixNamespaceComments: true
NamespaceIndentation: Inner
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: Yes # MultiLine
SpaceAfterTemplateKeyword: false
MaxEmptyLinesToKeep: 2
IndentPPDirectives: AfterHash
AlignEscapedNewlines: DontAlign
BreakConstructorInitializers: BeforeColon
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: true
# This one is mixed in its effect: it permits some single-line functions,
# but also tends to put, e.g., enums on a single line.
AllowShortBlocksOnASingleLine: true
AllowShortLoopsOnASingleLine: false
AlwaysBreakTemplateDeclarations: Yes # MultiLine
BreakConstructorInitializers: BeforeColon
DerivePointerAlignment: true
FixNamespaceComments: true
IndentPPDirectives: AfterHash
MaxEmptyLinesToKeep: 2
NamespaceIndentation: Inner
SpaceAfterTemplateKeyword: false
# No way to remove all space around operators as seen in much of the code I looked at.
#SpaceBeforeAssignmentOperators: false
@ -41,3 +38,4 @@ SortUsingDeclarations: false
# Hard to tell what the desired config here was.
# AllowShortFunctionsOnASingleLine: Inline
AllowShortFunctionsOnASingleLine: None

View File

@ -5,6 +5,7 @@ Changelog
---
New sketch features:
* New groups, revolution and helical extrusion.
* Extrude, lathe, translate and rotate groups can use the "assembly"
boolean operation, to increase performance.
* The solid model of extrude and lathe groups can be suppressed,
@ -52,6 +53,7 @@ New export/import features:
exported. This format allows to easily hack on triangle mesh data created
in SolveSpace, supports colour information and is more space efficient than
most other formats.
* VRML (WRL) triangle meshes can now be exported, useful for e.g. [KiCAD](http://kicad.org).
* Export 2d section: custom styled entities that lie in the same
plane as the exported section are included.
@ -68,6 +70,10 @@ New measurement/analysis features:
* New command for measuring center of mass, with live updates as the sketch
changes, "Analyze → Center of Mass".
* New option for displaying areas of closed contours.
* When calculating volume of the mesh, volume of the solid from the current
group is now shown alongside total volume of all solids.
* When calculating area, and faces are selected, calculate area of those faces
instead of the closed contour in the sketch.
* When selecting a point and a line, projected distance to current
workplane is displayed.

@ -1 +1 @@
Subproject commit fe71514837af1c627e9e1054d1aa0193a06d3488
Subproject commit 880db1d34706778f216a2308fd82a9a3adacb314

3
pkg/snap/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.snap
solvespace-snap-src
squashfs-root

12
pkg/snap/build.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh -xe
dir="$(dirname "$(readlink -f "$0")")"
solvespace_snap_src="$dir/solvespace-snap-src"
trap "rm -rf $solvespace_snap_src" EXIT
cd "$dir"
git_root="$(git rev-parse --show-toplevel)"
rsync --filter=":- .gitignore" -r "$git_root"/ "$solvespace_snap_src"
snapcraft "$@"

View File

@ -0,0 +1,76 @@
name: solvespace
base: core18
summary: Parametric 2d/3d CAD
adopt-info: solvespace
description: |
SOLVESPACE is a free (GPLv3) parametric 3d CAD tool.
Applications include
* modeling 3d parts — draw with extrudes, revolves, and Boolean (union / difference) operations
* modeling 2d parts — draw the part as a single section, and export DXF, PDF, SVG; use 3d assembly to verify fit
* 3d-printed parts — export the STL or other triangle mesh expected by most 3d printers
* preparing CAM data — export 2d vector art for a waterjet machine or laser cutter; or generate STEP or STL, for import into third-party CAM software for machining
* mechanism design — use the constraint solver to simulate planar or spatial linkages, with pin, ball, or slide joints
* plane and solid geometry — replace hand-solved trigonometry and spreadsheets with a live dimensioned drawing
confinement: strict
license: GPL-3.0
layout:
/usr/share/solvespace:
bind: $SNAP/usr/share/solvespace
apps:
solvespace:
command: usr/bin/solvespace
desktop: solvespace.desktop
extensions: [gnome-3-28]
plugs: [opengl, unity7, home, removable-media, gsettings, network]
environment:
__EGL_VENDOR_LIBRARY_DIRS: $SNAP/usr/share/glvnd/egl_vendor.d
cli:
command: usr/bin/solvespace-cli
plugs: [home, removable-media, network]
parts:
solvespace:
plugin: cmake
source: ./solvespace-snap-src
source-type: local
override-pull: |
snapcraftctl pull
version_major=$(grep "solvespace_VERSION_MAJOR" CMakeLists.txt | tr -d "()" | cut -d" " -f2)
version_minor=$(grep "solvespace_VERSION_MINOR" CMakeLists.txt | tr -d "()" | cut -d" " -f2)
version="$version_major.$version_minor~$(git rev-parse --short=8 HEAD)"
snapcraftctl set-version "$version"
git describe --exact-match HEAD && grade="stable" || grade="devel"
snapcraftctl set-grade "$grade"
git submodule update --init extlib/libdxfrw extlib/flatbuffers extlib/q3d
configflags:
- -DCMAKE_INSTALL_PREFIX=/usr
- -DCMAKE_BUILD_TYPE=Release
- -DENABLE_TESTS=OFF
- -DSNAP=ON
build-packages:
- zlib1g-dev
- libpng-dev
- libcairo2-dev
- libfreetype6-dev
- libjson-c-dev
- libfontconfig1-dev
- libgtkmm-3.0-dev
- libpangomm-1.4-dev
- libgl-dev
- libglu-dev
- libspnav-dev
- git
stage-packages:
- libspnav0
- libatkmm-1.6-1v5
- libcairomm-1.0-1v5
- libgtkmm-3.0-1v5
- libglibmm-2.4-1v5
- libpangomm-1.4-1v5
- libsigc++-2.0-0v5
- libglew2.0
- libegl-mesa0
- libdrm2

View File

@ -138,6 +138,13 @@ else()
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages
RENAME com.solvespace.SolveSpace-slvs.xml)
install(FILES freedesktop/solvespace-scalable.svg
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps
RENAME com.solvespace.SolveSpace.svg)
install(FILES freedesktop/solvespace-scalable.svg
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/mimetypes
RENAME com.solvespace.SolveSpace.svg)
foreach(SIZE 16x16 24x24 32x32 48x48)
install(FILES freedesktop/solvespace-${SIZE}.png
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/apps
@ -146,6 +153,22 @@ else()
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/mimetypes
RENAME com.solvespace.SolveSpace.png)
endforeach()
elseif(SNAP)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/freedesktop/solvespace-snap.desktop
DESTINATION /
RENAME solvespace.desktop)
# snapd does not support registering new mime types
install(FILES freedesktop/solvespace-scalable.svg
DESTINATION /meta/icons/hicolor/scalable/apps
RENAME snap.solvespace.svg)
foreach(SIZE 16x16 24x24 32x32 48x48)
install(FILES freedesktop/solvespace-${SIZE}.png
DESTINATION /meta/icons/hicolor/${SIZE}/apps
RENAME snap.solvespace.png)
endforeach()
else()
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/freedesktop/solvespace.desktop.in
@ -157,6 +180,13 @@ else()
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages
RENAME solvespace-slvs.xml)
install(FILES freedesktop/solvespace-scalable.svg
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps
RENAME solvespace.svg)
install(FILES freedesktop/solvespace-scalable.svg
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/mimetypes
RENAME application.x-solvespace.svg)
foreach(SIZE 16x16 24x24 32x32 48x48)
install(FILES freedesktop/solvespace-${SIZE}.png
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/apps

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<g>
<path d="m116 152h84v12h-84z" fill="#cbcbcb"/>
<path d="m152 80h12v74h-12z" fill="#cbcbcb"/>
<path d="m104 140h84v12h-84z" fill="#e40cf2"/>
<path d="m140 68h12v74h-12z" fill="#e40cf2"/>
<path d="m56 164h36v36h-36z" fill="#43f20c"/>
<path d="m68 32h12v192h-12z"/>
<path d="m32 176h192v12h-192z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 444 B

View File

@ -0,0 +1,10 @@
[Desktop Entry]
Version=1.0
Name=SolveSpace
Comment=A parametric 2d/3d CAD
Exec=solvespace
MimeType=application/x-solvespace
Icon=${SNAP}/meta/icons/hicolor/scalable/apps/snap.solvespace.svg
Type=Application
Categories=Graphics
Keywords=parametric;cad;2d;3d;

View File

@ -12,26 +12,19 @@ SBsp2 *SBsp2::Alloc() { return (SBsp2 *)AllocTemporary(sizeof(SBsp2)); }
SBsp3 *SBsp3::Alloc() { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); }
SBsp3 *SBsp3::FromMesh(const SMesh *m) {
SBsp3 *bsp3 = NULL;
int i;
SMesh mc = {};
for(i = 0; i < m->l.n; i++) {
mc.AddTriangle(&(m->l.elem[i]));
}
for(auto const &elt : m->l) { mc.AddTriangle(&elt); }
srand(0); // Let's be deterministic, at least!
int n = mc.l.n;
while(n > 1) {
int k = rand() % n;
n--;
swap(mc.l.elem[k], mc.l.elem[n]);
}
for(i = 0; i < mc.l.n; i++) {
bsp3 = InsertOrCreate(bsp3, &(mc.l.elem[i]), NULL);
swap(mc.l[k], mc.l[n]);
}
SBsp3 *bsp3 = NULL;
for(auto &elt : mc.l) { bsp3 = InsertOrCreate(bsp3, &elt, NULL); }
mc.Clear();
return bsp3;
}

View File

@ -12,16 +12,16 @@ void SolveSpaceUI::Clipboard::Clear() {
}
bool SolveSpaceUI::Clipboard::ContainsEntity(hEntity he) {
if(he.v == Entity::NO_ENTITY.v)
if(he == Entity::NO_ENTITY)
return true;
ClipboardRequest *cr;
for(cr = r.First(); cr; cr = r.NextAfter(cr)) {
if(cr->oldEnt.v == he.v)
if(cr->oldEnt == he)
return true;
for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
if(cr->oldPointEnt[i].v == he.v)
if(cr->oldPointEnt[i] == he)
return true;
}
}
@ -29,16 +29,16 @@ bool SolveSpaceUI::Clipboard::ContainsEntity(hEntity he) {
}
hEntity SolveSpaceUI::Clipboard::NewEntityFor(hEntity he) {
if(he.v == Entity::NO_ENTITY.v)
if(he == Entity::NO_ENTITY)
return Entity::NO_ENTITY;
ClipboardRequest *cr;
for(cr = r.First(); cr; cr = r.NextAfter(cr)) {
if(cr->oldEnt.v == he.v)
if(cr->oldEnt == he)
return cr->newReq.entity(0);
for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
if(cr->oldPointEnt[i].v == he.v)
if(cr->oldPointEnt[i] == he)
return cr->newReq.entity(1+i);
}
}
@ -170,9 +170,9 @@ void GraphicsWindow::PasteClipboard(Vector trans, double theta, double scale) {
hRequest hr = he.request();
Request *r = SK.GetRequest(hr);
if(r->type == Request::Type::ARC_OF_CIRCLE) {
if(he.v == hr.entity(2).v) {
if(he == hr.entity(2)) {
return hr.entity(3);
} else if(he.v == hr.entity(3).v) {
} else if(he == hr.entity(3)) {
return hr.entity(2);
}
}
@ -287,7 +287,7 @@ void GraphicsWindow::MenuClipboard(Command id) {
}
case Command::PASTE_TRANSFORM: {
if(SS.clipboard.r.n == 0) {
if(SS.clipboard.r.IsEmpty()) {
Error(_("Clipboard is empty; nothing to paste."));
break;
}

View File

@ -59,12 +59,12 @@ std::string Constraint::DescriptionString() const {
void Constraint::DeleteAllConstraintsFor(Constraint::Type type, hEntity entityA, hEntity ptA)
{
SK.constraint.ClearTags();
for(int i = 0; i < SK.constraint.n; i++) {
ConstraintBase *ct = &(SK.constraint.elem[i]);
for(auto &constraint : SK.constraint) {
ConstraintBase *ct = &constraint;
if(ct->type != type) continue;
if(ct->entityA.v != entityA.v) continue;
if(ct->ptA.v != ptA.v) continue;
if(ct->entityA != entityA) continue;
if(ct->ptA != ptA) continue;
ct->tag = 1;
}
SK.constraint.RemoveTagged();
@ -406,7 +406,7 @@ void Constraint::MenuConstrain(Command id) {
Entity *l0 = SK.GetEntity(gs.entity[0]),
*l1 = SK.GetEntity(gs.entity[1]);
if((l1->group.v != SS.GW.activeGroup.v) ||
if((l1->group != SS.GW.activeGroup) ||
(l1->construction && !(l0->construction)))
{
swap(l0, l1);
@ -433,10 +433,10 @@ void Constraint::MenuConstrain(Command id) {
"(symmetric about workplane)\n"));
return;
}
if(c.entityA.v == Entity::NO_ENTITY.v) {
if(c.entityA == Entity::NO_ENTITY) {
// Horizontal / vertical symmetry, implicit symmetry plane
// normal to the workplane
if(c.workplane.v == Entity::FREE_IN_3D.v) {
if(c.workplane == Entity::FREE_IN_3D) {
Error(_("A workplane must be active when constraining "
"symmetric without an explicit symmetry plane."));
return;
@ -466,7 +466,7 @@ void Constraint::MenuConstrain(Command id) {
case Command::VERTICAL:
case Command::HORIZONTAL: {
hEntity ha, hb;
if(c.workplane.v == Entity::FREE_IN_3D.v) {
if(c.workplane == Entity::FREE_IN_3D) {
Error(_("Activate a workplane (with Sketch -> In Workplane) before "
"applying a horizontal or vertical constraint."));
return;
@ -510,12 +510,10 @@ void Constraint::MenuConstrain(Command id) {
Entity *nfree = SK.GetEntity(c.entityA);
Entity *nref = SK.GetEntity(c.entityB);
if(nref->group.v == SS.GW.activeGroup.v) {
if(nref->group == SS.GW.activeGroup) {
swap(nref, nfree);
}
if(nfree->group.v == SS.GW.activeGroup.v &&
nref ->group.v != SS.GW.activeGroup.v)
{
if(nfree->group == SS.GW.activeGroup && nref->group != SS.GW.activeGroup) {
// nfree is free, and nref is locked (since it came from a
// previous group); so let's force nfree aligned to nref,
// and make convergence easy
@ -746,7 +744,7 @@ void Constraint::MenuConstrain(Command id) {
}
for(const Constraint &cc : SK.constraint) {
if(c.h.v != cc.h.v && c.Equals(cc)) {
if(c.h != cc.h && c.Equals(cc)) {
// Oops, we already have this exact constraint. Remove the one we just added.
SK.constraint.RemoveById(c.h);
SS.GW.ClearSelection();

View File

@ -40,7 +40,7 @@ Expr *ConstraintBase::PointLineDistance(hEntity wrkpl, hEntity hpt, hEntity hln)
EntityBase *p = SK.GetEntity(hpt);
if(wrkpl.v == EntityBase::FREE_IN_3D.v) {
if(wrkpl == EntityBase::FREE_IN_3D) {
ExprVector ep = p->PointGetExprs();
ExprVector ea = a->PointGetExprs();
@ -82,7 +82,7 @@ Expr *ConstraintBase::Distance(hEntity wrkpl, hEntity hpa, hEntity hpb) {
ssassert(pa->IsPoint() && pb->IsPoint(),
"Expected two points to measure projected distance between");
if(wrkpl.v == EntityBase::FREE_IN_3D.v) {
if(wrkpl == EntityBase::FREE_IN_3D) {
// This is true distance
ExprVector ea, eb, eab;
ea = pa->PointGetExprs();
@ -111,7 +111,7 @@ Expr *ConstraintBase::Distance(hEntity wrkpl, hEntity hpa, hEntity hpb) {
Expr *ConstraintBase::DirectionCosine(hEntity wrkpl,
ExprVector ae, ExprVector be)
{
if(wrkpl.v == EntityBase::FREE_IN_3D.v) {
if(wrkpl == EntityBase::FREE_IN_3D) {
Expr *mags = (ae.Magnitude())->Times(be.Magnitude());
return (ae.Dot(be))->Div(mags);
} else {
@ -146,7 +146,7 @@ void ConstraintBase::ModifyToSatisfy() {
Vector a = SK.GetEntity(entityA)->VectorGetNum();
Vector b = SK.GetEntity(entityB)->VectorGetNum();
if(other) a = a.ScaledBy(-1);
if(workplane.v != EntityBase::FREE_IN_3D.v) {
if(workplane != EntityBase::FREE_IN_3D) {
a = a.ProjectVectorInto(workplane);
b = b.ProjectVectorInto(workplane);
}
@ -172,7 +172,7 @@ void ConstraintBase::ModifyToSatisfy() {
// These equations are written in the form f(...) - d = 0, where
// d is the value of the valA.
valA += (l.elem[0].e)->Eval();
valA += (l[0].e)->Eval();
l.Clear();
}
@ -190,7 +190,7 @@ void ConstraintBase::AddEq(IdList<Equation,hEquation> *l, const ExprVector &v,
int baseIndex) const {
AddEq(l, v.x, baseIndex);
AddEq(l, v.y, baseIndex + 1);
if(workplane.v == EntityBase::FREE_IN_3D.v) {
if(workplane == EntityBase::FREE_IN_3D) {
AddEq(l, v.z, baseIndex + 2);
}
}
@ -200,7 +200,7 @@ void ConstraintBase::Generate(IdList<Param,hParam> *l) {
case Type::PARALLEL:
case Type::CUBIC_LINE_TANGENT:
// Add new parameter only when we operate in 3d space
if(workplane.v != EntityBase::FREE_IN_3D.v) break;
if(workplane != EntityBase::FREE_IN_3D) break;
// fallthrough
case Type::SAME_ORIENTATION:
case Type::PT_ON_LINE: {
@ -361,7 +361,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
case Type::POINTS_COINCIDENT: {
EntityBase *a = SK.GetEntity(ptA);
EntityBase *b = SK.GetEntity(ptB);
if(workplane.v == EntityBase::FREE_IN_3D.v) {
if(workplane == EntityBase::FREE_IN_3D) {
ExprVector pa = a->PointGetExprs();
ExprVector pb = b->PointGetExprs();
AddEq(l, pa.x->Minus(pb.x), 0);
@ -430,7 +430,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
}
case Type::AT_MIDPOINT:
if(workplane.v == EntityBase::FREE_IN_3D.v) {
if(workplane == EntityBase::FREE_IN_3D) {
EntityBase *ln = SK.GetEntity(entityA);
ExprVector a = SK.GetEntity(ln->point[0])->PointGetExprs();
ExprVector b = SK.GetEntity(ln->point[1])->PointGetExprs();
@ -469,7 +469,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
return;
case Type::SYMMETRIC:
if(workplane.v == EntityBase::FREE_IN_3D.v) {
if(workplane == EntityBase::FREE_IN_3D) {
EntityBase *plane = SK.GetEntity(entityA);
EntityBase *ea = SK.GetEntity(ptA);
EntityBase *eb = SK.GetEntity(ptB);
@ -521,7 +521,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
case Type::SYMMETRIC_HORIZ:
case Type::SYMMETRIC_VERT: {
ssassert(workplane.v != Entity::FREE_IN_3D.v,
ssassert(workplane != Entity::FREE_IN_3D,
"Unexpected horizontal/vertical symmetric constraint in 3d");
EntityBase *a = SK.GetEntity(ptA);
@ -576,7 +576,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
case Type::HORIZONTAL:
case Type::VERTICAL: {
ssassert(workplane.v != Entity::FREE_IN_3D.v,
ssassert(workplane != Entity::FREE_IN_3D,
"Unexpected horizontal/vertical constraint in 3d");
hEntity ha, hb;
@ -701,7 +701,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
ExprVector b = line->VectorGetExprs();
if(workplane.v == EntityBase::FREE_IN_3D.v) {
if(workplane == EntityBase::FREE_IN_3D) {
ExprVector eq = VectorsParallel3d(a, b, valP);
AddEq(l, eq);
} else {
@ -755,7 +755,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
ExprVector a = ea->VectorGetExprsInWorkplane(workplane);
ExprVector b = eb->VectorGetExprsInWorkplane(workplane);
if(workplane.v == EntityBase::FREE_IN_3D.v) {
if(workplane == EntityBase::FREE_IN_3D) {
ExprVector eq = VectorsParallel3d(a, b, valP);
AddEq(l, eq);
} else {
@ -775,7 +775,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
case Type::WHERE_DRAGGED: {
EntityBase *ep = SK.GetEntity(ptA);
if(workplane.v == EntityBase::FREE_IN_3D.v) {
if(workplane == EntityBase::FREE_IN_3D) {
ExprVector ev = ep->PointGetExprs();
Vector v = ep->PointGetNum();

View File

@ -35,7 +35,7 @@ void TextWindow::ScreenSetTtfFont(int link, uint32_t v) {
if(!r) return;
SS.UndoRemember();
r->font = SS.fonts.l.elem[i].FontFileBaseName();
r->font = SS.fonts.l[i].FontFileBaseName();
SS.MarkGroupDirty(r->group);
SS.ScheduleShowTW();
}
@ -175,9 +175,9 @@ void TextWindow::DescribeSelection() {
e->str.c_str(), &ScreenEditTtfText, e->h.request().v);
Printf(true, " select new font");
SS.fonts.LoadAll();
int i;
for(i = 0; i < SS.fonts.l.n; i++) {
TtfFont *tf = &(SS.fonts.l.elem[i]);
// Not using range-for here because we use i inside the output.
for(int i = 0; i < SS.fonts.l.n; i++) {
TtfFont *tf = &(SS.fonts.l[i]);
if(e->font == tf->FontFileBaseName()) {
Printf(false, "%Bp %s",
(i & 1) ? 'd' : 'a',
@ -214,7 +214,7 @@ void TextWindow::DescribeSelection() {
Group *g = SK.GetGroup(e->group);
Printf(false, "");
Printf(false, "%FtIN GROUP%E %s", g->DescriptionString().c_str());
if(e->workplane.v == Entity::FREE_IN_3D.v) {
if(e->workplane == Entity::FREE_IN_3D) {
Printf(false, "%FtNOT LOCKED IN WORKPLANE%E");
} else {
Entity *w = SK.GetEntity(e->workplane);
@ -232,12 +232,13 @@ void TextWindow::DescribeSelection() {
std::vector<hConstraint> lhc = {};
for(const Constraint &c : SK.constraint) {
if(!(c.ptA.v == e->h.v ||
c.ptB.v == e->h.v ||
c.entityA.v == e->h.v ||
c.entityB.v == e->h.v ||
c.entityC.v == e->h.v ||
c.entityD.v == e->h.v)) continue;
if(!(c.ptA == e->h ||
c.ptB == e->h ||
c.entityA == e->h ||
c.entityB == e->h ||
c.entityC == e->h ||
c.entityD == e->h))
continue;
lhc.push_back(c.h);
}
@ -314,8 +315,7 @@ void TextWindow::DescribeSelection() {
Printf(true, " pt-ln distance = %Fi%s%E",
SS.MmToString(pp.DistanceToLine(lp0, lp1.Minus(lp0))).c_str());
hEntity wrkpl = SS.GW.ActiveWorkplane();
if(wrkpl.v != Entity::FREE_IN_3D.v &&
!(p->workplane.v == wrkpl.v && ln->workplane.v == wrkpl.v)) {
if(wrkpl != Entity::FREE_IN_3D && !(p->workplane == wrkpl && ln->workplane == wrkpl)) {
Vector ppw = pp.ProjectInto(wrkpl);
Vector lp0w = lp0.ProjectInto(wrkpl);
Vector lp1w = lp1.ProjectInto(wrkpl);
@ -385,10 +385,9 @@ void TextWindow::DescribeSelection() {
lhe.push_back(c->entityC);
lhe.push_back(c->entityD);
auto it = std::remove_if(lhe.begin(), lhe.end(),
[](hEntity he) {
return he.v == Entity::NO_ENTITY.v || !he.isFromRequest();
});
auto it = std::remove_if(lhe.begin(), lhe.end(), [](hEntity he) {
return he == Entity::NO_ENTITY || !he.isFromRequest();
});
lhe.erase(it, lhe.end());
if(!lhe.empty()) {

View File

@ -8,8 +8,8 @@
#include "solvespace.h"
bool GraphicsWindow::Selection::Equals(Selection *b) {
if(entity.v != b->entity.v) return false;
if(constraint.v != b->constraint.v) return false;
if(entity != b->entity) return false;
if(constraint != b->constraint) return false;
return true;
}
@ -151,7 +151,8 @@ void GraphicsWindow::MakeUnselected(Selection *stog, bool coincidentPointTrick){
Vector ep = e->PointGetNum();
for(s = selection.First(); s; s = selection.NextAfter(s)) {
if(!s->entity.v) continue;
if(s->entity.v == stog->entity.v) continue;
if(s->entity == stog->entity)
continue;
Entity *se = SK.GetEntity(s->entity);
if(!se->IsPoint()) continue;
if(ep.Equals(se->PointGetNum())) {
@ -211,7 +212,7 @@ void GraphicsWindow::SelectByMarquee() {
Entity *e;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
if(e->group.v != SS.GW.activeGroup.v) continue;
if(e->group != SS.GW.activeGroup) continue;
if(e->IsFace() || e->IsDistance()) continue;
if(!e->IsVisible()) continue;
@ -232,7 +233,7 @@ void GraphicsWindow::GroupSelection() {
gs = {};
int i;
for(i = 0; i < selection.n; i++) {
Selection *s = &(selection.elem[i]);
Selection *s = &(selection[i]);
if(s->entity.v) {
(gs.n)++;
@ -328,7 +329,8 @@ Lighting GraphicsWindow::GetLighting() const {
GraphicsWindow::Selection GraphicsWindow::ChooseFromHoverToSelect() {
Selection sel = {};
if(hoverList.n == 0) return sel;
if(hoverList.IsEmpty())
return sel;
Group *activeGroup = SK.GetGroup(SS.GW.activeGroup);
int bestOrder = -1;
@ -701,7 +703,7 @@ void GraphicsWindow::Draw(Canvas *canvas) {
if(SS.showContourAreas) {
for(hGroup hg : SK.groupOrder) {
Group *g = SK.GetGroup(hg);
if(g->h.v != activeGroup.v) continue;
if(g->h != activeGroup) continue;
if(!(g->IsVisible())) continue;
g->DrawContourAreaLabels(canvas);
}

View File

@ -148,7 +148,7 @@ int Constraint::DoLineTrimmedAgainstBox(Canvas *canvas, Canvas::hStroke hcs,
}
if(j < 4) continue;
double t = (p.Minus(a)).DivPivoting(dl);
double t = (p.Minus(a)).DivProjected(dl);
tmin = min(t, tmin);
tmax = max(t, tmax);
}
@ -298,7 +298,7 @@ void Constraint::DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
Vector gr = camera.projRight.ScaledBy(1.0);
Vector gu = camera.projUp.ScaledBy(1.0);
if(workplane.v != Entity::FREE_IN_3D.v) {
if(workplane != Entity::FREE_IN_3D) {
a0 = a0.ProjectInto(workplane);
b0 = b0.ProjectInto(workplane);
da = da.ProjectVectorInto(workplane);
@ -452,7 +452,7 @@ bool Constraint::IsVisible() const {
if(!(g->visible)) return false;
// And likewise if the group is not the active group; except for comments
// with an assigned style.
if(g->h.v != SS.GW.activeGroup.v && !(type == Type::COMMENT && disp.style.v)) {
if(g->h != SS.GW.activeGroup && !(type == Type::COMMENT && disp.style.v)) {
return false;
}
if(disp.style.v) {
@ -529,7 +529,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Vector ap = SK.GetEntity(ptA)->PointGetNum();
Vector bp = SK.GetEntity(ptB)->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) {
if(workplane != Entity::FREE_IN_3D) {
DoProjectedPoint(canvas, hcs, &ap);
DoProjectedPoint(canvas, hcs, &bp);
}
@ -596,7 +596,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Vector lB = SK.GetEntity(line->point[1])->PointGetNum();
Vector dl = lB.Minus(lA);
if(workplane.v != Entity::FREE_IN_3D.v) {
if(workplane != Entity::FREE_IN_3D) {
lA = lA.ProjectInto(workplane);
lB = lB.ProjectInto(workplane);
DoProjectedPoint(canvas, hcs, &pt);
@ -638,11 +638,11 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
}
}
if(workplane.v != Entity::FREE_IN_3D.v) {
if(workplane != Entity::FREE_IN_3D) {
// Draw the projection marker from the closest point on the
// projected line to the projected point on the real line.
Vector lAB = (lA.Minus(lB));
double t = (lA.Minus(closest)).DivPivoting(lAB);
double t = (lA.Minus(closest)).DivProjected(lAB);
Vector lA = SK.GetEntity(line->point[0])->PointGetNum();
Vector lB = SK.GetEntity(line->point[1])->PointGetNum();
@ -829,7 +829,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
case Type::PERPENDICULAR: {
Vector u = Vector::From(0, 0, 0), v = Vector::From(0, 0, 0);
Vector rn, ru;
if(workplane.v == Entity::FREE_IN_3D.v) {
if(workplane == Entity::FREE_IN_3D) {
rn = gn;
ru = gu;
} else {
@ -882,7 +882,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
v = norm->NormalV();
} else if(type == Type::CUBIC_LINE_TANGENT) {
Vector n;
if(workplane.v == Entity::FREE_IN_3D.v) {
if(workplane == Entity::FREE_IN_3D) {
u = gr;
v = gu;
n = gn;
@ -985,7 +985,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
a = SK.GetEntity(e->point[0])->PointGetNum();
b = SK.GetEntity(e->point[1])->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) {
if(workplane != Entity::FREE_IN_3D) {
DoProjectedPoint(canvas, hcs, &a);
DoProjectedPoint(canvas, hcs, &b);
}
@ -1005,7 +1005,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Entity *forLen = SK.GetEntity(entityA);
Vector a = SK.GetEntity(forLen->point[0])->PointGetNum(),
b = SK.GetEntity(forLen->point[1])->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) {
if(workplane != Entity::FREE_IN_3D) {
DoProjectedPoint(canvas, hcs, &a);
DoProjectedPoint(canvas, hcs, &b);
}
@ -1017,7 +1017,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Vector la = SK.GetEntity(ln->point[0])->PointGetNum(),
lb = SK.GetEntity(ln->point[1])->PointGetNum();
Vector pt = SK.GetEntity(ptA)->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) {
if(workplane != Entity::FREE_IN_3D) {
DoProjectedPoint(canvas, hcs, &pt);
la = la.ProjectInto(workplane);
lb = lb.ProjectInto(workplane);
@ -1039,7 +1039,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
Entity *pte = SK.GetEntity(i == 0 ? ptA : ptB);
Vector pt = pte->PointGetNum();
if(workplane.v != Entity::FREE_IN_3D.v) {
if(workplane != Entity::FREE_IN_3D) {
DoProjectedPoint(canvas, hcs, &pt);
la = la.ProjectInto(workplane);
lb = lb.ProjectInto(workplane);
@ -1104,7 +1104,7 @@ s:
case Type::VERTICAL:
if(entityA.v) {
Vector r, u, n;
if(workplane.v == Entity::FREE_IN_3D.v) {
if(workplane == Entity::FREE_IN_3D) {
r = gr; u = gu; n = gn;
} else {
r = SK.GetEntity(workplane)->Normal()->NormalU();
@ -1171,7 +1171,7 @@ s:
case Type::COMMENT: {
Vector u, v;
if(workplane.v == Entity::FREE_IN_3D.v) {
if(workplane == Entity::FREE_IN_3D) {
u = gr;
v = gu;
} else {

View File

@ -20,32 +20,31 @@ std::string Entity::DescriptionString() const {
void Entity::GenerateEdges(SEdgeList *el) {
SBezierList *sbl = GetOrGenerateBezierCurves();
int i, j;
for(i = 0; i < sbl->l.n; i++) {
SBezier *sb = &(sbl->l.elem[i]);
for(int i = 0; i < sbl->l.n; i++) {
SBezier *sb = &(sbl->l[i]);
List<Vector> lv = {};
sb->MakePwlInto(&lv);
for(j = 1; j < lv.n; j++) {
el->AddEdge(lv.elem[j-1], lv.elem[j], style.v, i);
for(int j = 1; j < lv.n; j++) {
el->AddEdge(lv[j-1], lv[j], style.v, i);
}
lv.Clear();
}
}
SBezierList *Entity::GetOrGenerateBezierCurves() {
if(beziers.l.n == 0)
if(beziers.l.IsEmpty())
GenerateBezierCurves(&beziers);
return &beziers;
}
SEdgeList *Entity::GetOrGenerateEdges() {
if(edges.l.n != 0) {
if(!edges.l.IsEmpty()) {
if(EXACT(edgesChordTol == SS.ChordTolMm()))
return &edges;
edges.l.Clear();
}
if(edges.l.n == 0)
if(edges.l.IsEmpty())
GenerateEdges(&edges);
edgesChordTol = SS.ChordTolMm();
return &edges;
@ -55,7 +54,7 @@ BBox Entity::GetOrGenerateScreenBBox(bool *hasBBox) {
SBezierList *sbl = GetOrGenerateBezierCurves();
// We don't bother with bounding boxes for workplanes, etc.
*hasBBox = (IsPoint() || IsNormal() || sbl->l.n > 0);
*hasBBox = (IsPoint() || IsNormal() || !sbl->l.IsEmpty());
if(!*hasBBox) return {};
if(screenBBoxValid)
@ -67,16 +66,14 @@ BBox Entity::GetOrGenerateScreenBBox(bool *hasBBox) {
} else if(IsNormal()) {
Vector proj = SK.GetEntity(point[0])->PointGetNum();
screenBBox = BBox::From(proj, proj);
} else if(sbl->l.n > 0) {
Vector first = SS.GW.ProjectPoint3(sbl->l.elem[0].ctrl[0]);
} else if(!sbl->l.IsEmpty()) {
Vector first = SS.GW.ProjectPoint3(sbl->l[0].ctrl[0]);
screenBBox = BBox::From(first, first);
for(int i = 0; i < sbl->l.n; i++) {
SBezier *sb = &sbl->l.elem[i];
for(int i = 0; i <= sb->deg; i++) {
screenBBox.Include(SS.GW.ProjectPoint3(sb->ctrl[i]));
}
for(auto &sb : sbl->l) {
for(int i = 0; i < sb.deg; ++i) { screenBBox.Include(SS.GW.ProjectPoint3(sb.ctrl[i])); }
}
} else ssassert(false, "Expected entity to be a point or have beziers");
} else
ssassert(false, "Expected entity to be a point or have beziers");
screenBBoxValid = true;
return screenBBox;
@ -88,6 +85,7 @@ void Entity::GetReferencePoints(std::vector<Vector> *refs) {
case Type::POINT_N_TRANS:
case Type::POINT_N_ROT_TRANS:
case Type::POINT_N_ROT_AA:
case Type::POINT_N_ROT_AXIS_TRANS:
case Type::POINT_IN_3D:
case Type::POINT_IN_2D:
refs->push_back(PointGetNum());
@ -150,7 +148,7 @@ bool Entity::IsStylable() const {
bool Entity::IsVisible() const {
Group *g = SK.GetGroup(group);
if(g->h.v == Group::HGROUP_REFERENCES.v && IsNormal()) {
if(g->h == Group::HGROUP_REFERENCES && IsNormal()) {
// The reference normals are always shown
return true;
}
@ -162,7 +160,7 @@ bool Entity::IsVisible() const {
if(!SS.GW.showWorkplanes) {
if(IsWorkplane() && !h.isFromRequest()) {
if(g->h.v != SS.GW.activeGroup.v) {
if(g->h != SS.GW.activeGroup) {
// The group-associated workplanes are hidden outside
// their group.
return false;
@ -440,7 +438,7 @@ void Entity::GenerateBezierCurves(SBezierList *sbl) const {
// Record our style for all of the Beziers that we just created.
for(; i < sbl->l.n; i++) {
sbl->l.elem[i].auxA = style.v;
sbl->l[i].auxA = style.v;
}
}
@ -453,7 +451,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
zIndex = 5;
} else if(how == DrawAs::HIDDEN) {
zIndex = 2;
} else if(group.v != SS.GW.activeGroup.v) {
} else if(group != SS.GW.activeGroup) {
zIndex = 3;
} else {
zIndex = 4;
@ -502,6 +500,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
case Type::POINT_N_TRANS:
case Type::POINT_N_ROT_TRANS:
case Type::POINT_N_ROT_AA:
case Type::POINT_N_ROT_AXIS_TRANS:
case Type::POINT_IN_3D:
case Type::POINT_IN_2D: {
if(how == DrawAs::HIDDEN) return;
@ -568,11 +567,11 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
// dimmer for the ones at the model origin.
hRequest hr = h.request();
uint8_t luma = (asReference) ? 255 : 100;
if(hr.v == Request::HREQUEST_REFERENCE_XY.v) {
if(hr == Request::HREQUEST_REFERENCE_XY) {
stroke.color = RgbaColor::From(0, 0, luma);
} else if(hr.v == Request::HREQUEST_REFERENCE_YZ.v) {
} else if(hr == Request::HREQUEST_REFERENCE_YZ) {
stroke.color = RgbaColor::From(luma, 0, 0);
} else if(hr.v == Request::HREQUEST_REFERENCE_ZX.v) {
} else if(hr == Request::HREQUEST_REFERENCE_ZX) {
stroke.color = RgbaColor::From(0, luma, 0);
}
}
@ -587,6 +586,11 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
double s = camera.scale;
double h = 60 - camera.height / 2.0;
double w = 60 - camera.width / 2.0;
// Shift the axis to the right if they would overlap with the toolbar.
if(SS.showToolbar) {
if(h + 30 > -(34*16 + 3*16 + 8) / 2)
w += 60;
}
tail = camera.projRight.ScaledBy(w/s).Plus(
camera.projUp. ScaledBy(h/s)).Minus(camera.offset);
} else {

279
src/dsc.h
View File

@ -9,6 +9,34 @@
#include "solvespace.h"
#include <type_traits>
/// Trait indicating which types are handle types and should get the associated operators.
/// Specialize for each handle type and inherit from std::true_type.
template<typename T>
struct IsHandleOracle : std::false_type {};
// Equality-compare any two instances of a handle type.
template<typename T>
static inline typename std::enable_if<IsHandleOracle<T>::value, bool>::type
operator==(T const &lhs, T const &rhs) {
return lhs.v == rhs.v;
}
// Inequality-compare any two instances of a handle type.
template<typename T>
static inline typename std::enable_if<IsHandleOracle<T>::value, bool>::type
operator!=(T const &lhs, T const &rhs) {
return !(lhs == rhs);
}
// Less-than-compare any two instances of a handle type.
template<typename T>
static inline typename std::enable_if<IsHandleOracle<T>::value, bool>::type
operator<(T const &lhs, T const &rhs) {
return lhs.v < rhs.v;
}
class Vector;
class Vector4;
class Point2d;
@ -92,7 +120,7 @@ public:
Vector ScaledBy(double s) const;
Vector ProjectInto(hEntity wrkpl) const;
Vector ProjectVectorInto(hEntity wrkpl) const;
double DivPivoting(Vector delta) const;
double DivProjected(Vector delta) const;
Vector ClosestOrtho() const;
void MakeMaxMin(Vector *maxv, Vector *minv) const;
Vector ClampWithin(double minv, double maxv) const;
@ -159,7 +187,7 @@ public:
Point2d Plus(const Point2d &b) const;
Point2d Minus(const Point2d &b) const;
Point2d ScaledBy(double s) const;
double DivPivoting(Point2d delta) const;
double DivProjected(Point2d delta) const;
double Dot(Point2d p) const;
double DistanceTo(const Point2d &p) const;
double DistanceToLine(const Point2d &p0, const Point2d &dp, bool asSegment) const;
@ -174,17 +202,20 @@ public:
};
// A simple list
template <class T>
template<class T>
class List {
T *elem = nullptr;
int elemsAllocated = 0;
public:
T *elem;
int n;
int elemsAllocated;
int n = 0;
bool IsEmpty() const { return n == 0; }
void ReserveMore(int howMuch) {
if(n + howMuch > elemsAllocated) {
elemsAllocated = n + howMuch;
T *newElem = (T *)MemAlloc((size_t)elemsAllocated*sizeof(elem[0]));
T *newElem = (T *)MemAlloc((size_t)elemsAllocated*sizeof(T));
for(int i = 0; i < n; i++) {
new(&newElem[i]) T(std::move(elem[i]));
elem[i].~T();
@ -214,31 +245,41 @@ public:
}
T *First() {
return (n == 0) ? NULL : &(elem[0]);
return IsEmpty() ? nullptr : &(elem[0]);
}
const T *First() const {
return (n == 0) ? NULL : &(elem[0]);
return IsEmpty() ? nullptr : &(elem[0]);
}
T *Last() { return IsEmpty() ? nullptr : &(elem[n - 1]); }
const T *Last() const { return IsEmpty() ? nullptr : &(elem[n - 1]); }
T *NextAfter(T *prev) {
if(!prev) return NULL;
if(prev - elem == (n - 1)) return NULL;
if(IsEmpty() || !prev) return NULL;
if(prev - First() == (n - 1)) return NULL;
return prev + 1;
}
const T *NextAfter(const T *prev) const {
if(!prev) return NULL;
if(prev - elem == (n - 1)) return NULL;
if(IsEmpty() || !prev) return NULL;
if(prev - First() == (n - 1)) return NULL;
return prev + 1;
}
T *begin() { return &elem[0]; }
T *end() { return &elem[n]; }
const T *begin() const { return &elem[0]; }
const T *end() const { return &elem[n]; }
T &Get(size_t i) { return elem[i]; }
T const &Get(size_t i) const { return elem[i]; }
T &operator[](size_t i) { return Get(i); }
T const &operator[](size_t i) const { return Get(i); }
T *begin() { return IsEmpty() ? nullptr : &elem[0]; }
T *end() { return IsEmpty() ? nullptr : &elem[n]; }
const T *begin() const { return IsEmpty() ? nullptr : &elem[0]; }
const T *end() const { return IsEmpty() ? nullptr : &elem[n]; }
const T *cbegin() const { return begin(); }
const T *cend() const { return end(); }
void ClearTags() {
int i;
for(i = 0; i < n; i++) {
elem[i].tag = 0;
for(auto & elt : *this) {
elt.tag = 0;
}
}
@ -251,21 +292,20 @@ public:
}
void RemoveTagged() {
int src, dest;
dest = 0;
for(src = 0; src < n; src++) {
if(elem[src].tag) {
// this item should be deleted
} else {
if(src != dest) {
elem[dest] = elem[src];
}
dest++;
auto newEnd = std::remove_if(this->begin(), this->end(), [](T &t) {
if(t.tag) {
return true;
}
return false;
});
auto oldEnd = this->end();
n = newEnd - begin();
if (newEnd != nullptr && oldEnd != nullptr) {
while(newEnd != oldEnd) {
newEnd->~T();
++newEnd;
}
}
for(int i = dest; i < n; i++)
elem[i].~T();
n = dest;
// and elemsAllocated is untouched, because we didn't resize
}
@ -285,21 +325,44 @@ public:
}
};
// Comparison functor used by IdList and related classes
template <class T, class H>
struct CompareId {
bool operator()(T const& lhs, T const& rhs) const {
return lhs.h.v < rhs.h.v;
}
bool operator()(T const& lhs, H rhs) const {
return lhs.h.v < rhs.v;
}
};
// A list, where each element has an integer identifier. The list is kept
// sorted by that identifier, and items can be looked up in log n time by
// id.
template <class T, class H>
class IdList {
T *elem = nullptr;
int elemsAllocated = 0;
public:
T *elem;
int n;
int elemsAllocated;
int n = 0;
using Compare = CompareId<T, H>;
bool IsEmpty() const {
return n == 0;
}
void AllocForOneMore() {
if(n >= elemsAllocated) {
ReserveMore((elemsAllocated + 32)*2 - n);
}
}
uint32_t MaximumId() {
if(n == 0) {
if(IsEmpty()) {
return 0;
} else {
return elem[n - 1].h.v;
return Last()->h.v;
}
}
@ -310,10 +373,35 @@ public:
return t->h;
}
T * LowerBound(T const& t) {
if(IsEmpty()) {
return nullptr;
}
auto it = std::lower_bound(begin(), end(), t, Compare());
return it;
}
T * LowerBound(H const& h) {
if(IsEmpty()) {
return nullptr;
}
auto it = std::lower_bound(begin(), end(), h, Compare());
return it;
}
int LowerBoundIndex(T const& t) {
if(IsEmpty()) {
return 0;
}
auto it = LowerBound(t);
auto idx = std::distance(begin(), it);
auto i = static_cast<int>(idx);
return i;
}
void ReserveMore(int howMuch) {
if(n + howMuch > elemsAllocated) {
elemsAllocated = n + howMuch;
T *newElem = (T *)MemAlloc((size_t)elemsAllocated*sizeof(elem[0]));
T *newElem = (T *)MemAlloc((size_t)elemsAllocated*sizeof(T));
for(int i = 0; i < n; i++) {
new(&newElem[i]) T(std::move(elem[i]));
elem[i].~T();
@ -324,28 +412,16 @@ public:
}
void Add(T *t) {
if(n >= elemsAllocated) {
ReserveMore((elemsAllocated + 32)*2 - n);
}
AllocForOneMore();
int first = 0, last = n;
// We know that we must insert within the closed interval [first,last]
while(first != last) {
int mid = (first + last)/2;
H hm = elem[mid].h;
ssassert(hm.v != t->h.v, "Handle isn't unique");
if(hm.v > t->h.v) {
last = mid;
} else if(hm.v < t->h.v) {
first = mid + 1;
}
}
// Look to see if we already have something with the same handle value.
ssassert(FindByIdNoOops(t->h) == nullptr, "Handle isn't unique");
int i = first;
new(&elem[n]) T();
std::move_backward(elem + i, elem + n, elem + n + 1);
elem[i] = *t;
n++;
// Copy-construct at the end of the list.
new(&elem[n]) T(*t);
++n;
// The item we just added is trivially sorted, so "merge"
std::inplace_merge(begin(), end() - 1, end(), Compare());
}
T *FindById(H h) {
@ -355,50 +431,54 @@ public:
}
int IndexOf(H h) {
int first = 0, last = n-1;
while(first <= last) {
int mid = (first + last)/2;
H hm = elem[mid].h;
if(hm.v > h.v) {
last = mid-1; // and first stays the same
} else if(hm.v < h.v) {
first = mid+1; // and last stays the same
} else {
return mid;
}
if(IsEmpty()) {
return -1;
}
auto it = LowerBound(h);
auto idx = std::distance(begin(), it);
if (idx < n) {
return idx;
}
return -1;
}
T *FindByIdNoOops(H h) {
int first = 0, last = n-1;
while(first <= last) {
int mid = (first + last)/2;
H hm = elem[mid].h;
if(hm.v > h.v) {
last = mid-1; // and first stays the same
} else if(hm.v < h.v) {
first = mid+1; // and last stays the same
} else {
return &(elem[mid]);
}
if(IsEmpty()) {
return nullptr;
}
return NULL;
auto it = LowerBound(h);
if (it == nullptr || it == end()) {
return nullptr;
}
if (it->h.v == h.v) {
return it;
}
return nullptr;
}
T *First() {
return (n == 0) ? NULL : &(elem[0]);
return (IsEmpty()) ? NULL : &(elem[0]);
}
T *Last() {
return (IsEmpty()) ? NULL : &(elem[n-1]);
}
T *NextAfter(T *prev) {
if(!prev) return NULL;
if(prev - elem == (n - 1)) return NULL;
if(IsEmpty() || !prev) return NULL;
if(prev - First() == (n - 1)) return NULL;
return prev + 1;
}
T *begin() { return &elem[0]; }
T *end() { return &elem[n]; }
const T *begin() const { return &elem[0]; }
const T *end() const { return &elem[n]; }
T &Get(size_t i) { return elem[i]; }
T const &Get(size_t i) const { return elem[i]; }
T &operator[](size_t i) { return Get(i); }
T const &operator[](size_t i) const { return Get(i); }
T *begin() { return IsEmpty() ? nullptr : &elem[0]; }
T *end() { return IsEmpty() ? nullptr : &elem[0] + n; }
const T *begin() const { return IsEmpty() ? nullptr : &elem[0]; }
const T *end() const { return IsEmpty() ? nullptr : &elem[0] + n; }
const T *cbegin() const { return begin(); }
const T *cend() const { return end(); }
template<typename F>
size_t CountIf(F &&predicate) const {
@ -406,18 +486,13 @@ public:
}
void ClearTags() {
int i;
for(i = 0; i < n; i++) {
elem[i].tag = 0;
}
for(auto &elt : *this) { elt.tag = 0; }
}
void Tag(H h, int tag) {
int i;
for(i = 0; i < n; i++) {
if(elem[i].h.v == h.v) {
elem[i].tag = tag;
}
auto it = FindByIdNoOops(h);
if (it != nullptr) {
it->tag = tag;
}
}
@ -448,9 +523,9 @@ public:
void MoveSelfInto(IdList<T,H> *l) {
l->Clear();
*l = *this;
elemsAllocated = n = 0;
elem = NULL;
std::swap(l->elem, elem);
std::swap(l->elemsAllocated, elemsAllocated);
std::swap(l->n, n);
}
void DeepCopyInto(IdList<T,H> *l) {
@ -467,9 +542,9 @@ public:
elem[i].Clear();
elem[i].~T();
}
elemsAllocated = n = 0;
if(elem) MemFree(elem);
elem = NULL;
elemsAllocated = n = 0;
}
};

View File

@ -37,7 +37,7 @@ ExprVector EntityBase::VectorGetExprsInWorkplane(hEntity wrkpl) const {
case Type::NORMAL_N_ROT:
case Type::NORMAL_N_ROT_AA: {
ExprVector ev = NormalExprsN();
if(wrkpl.v == EntityBase::FREE_IN_3D.v) {
if(wrkpl == EntityBase::FREE_IN_3D) {
return ev;
}
// Get the offset and basis vectors for this weird exotic csys.
@ -245,6 +245,7 @@ bool EntityBase::IsPoint() const {
case Type::POINT_N_TRANS:
case Type::POINT_N_ROT_TRANS:
case Type::POINT_N_ROT_AA:
case Type::POINT_N_ROT_AXIS_TRANS:
return true;
default:
@ -454,10 +455,38 @@ void EntityBase::PointForceTo(Vector p) {
// in order to avoid jumps when you cross from +pi to -pi
while(dtheta < -PI) dtheta += 2*PI;
while(dtheta > PI) dtheta -= 2*PI;
// this extra *2 explains the mystery *4
SK.GetParam(param[3])->val = (thetai + dtheta)/(timesApplied*2);
break;
}
case Type::POINT_N_ROT_AXIS_TRANS: {
if(timesApplied == 0) break;
// is the point on the rotation axis?
Vector offset = Vector::From(param[0], param[1], param[2]);
Vector normal = Vector::From(param[4], param[5], param[6]).WithMagnitude(1.0);
Vector check = numPoint.Minus(offset).Cross(normal);
if (check.Dot(check) < LENGTH_EPS) { // if so, do extrusion style drag
Vector trans = (p.Minus(numPoint));
SK.GetParam(param[7])->val = trans.Dot(normal)/timesApplied;
} else { // otherwise do rotation style
Vector u = normal.Normal(0), v = normal.Normal(1);
Vector po = p.Minus(offset), numo = numPoint.Minus(offset);
double thetap = atan2(v.Dot(po), u.Dot(po));
double thetan = atan2(v.Dot(numo), u.Dot(numo));
double thetaf = (thetap - thetan);
double thetai = (SK.GetParam(param[3])->val)*timesApplied*2;
double dtheta = thetaf - thetai;
// Take the smallest possible change in the actual step angle,
// in order to avoid jumps when you cross from +pi to -pi
while(dtheta < -PI) dtheta += 2*PI;
while(dtheta > PI) dtheta -= 2*PI;
// this extra *2 explains the mystery *4
SK.GetParam(param[3])->val = (thetai + dtheta)/(timesApplied*2);
}
break;
}
case Type::POINT_N_COPY:
// Nothing to do; it's a static copy
break;
@ -506,6 +535,17 @@ Vector EntityBase::PointGetNum() const {
break;
}
case Type::POINT_N_ROT_AXIS_TRANS: {
Vector offset = Vector::From(param[0], param[1], param[2]);
Vector displace = Vector::From(param[4], param[5], param[6])
.WithMagnitude(SK.GetParam(param[7])->val).ScaledBy(timesApplied);
Quaternion q = PointGetQuaternion();
p = numPoint.Minus(offset);
p = q.Rotate(p);
p = p.Plus(offset).Plus(displace);
break;
}
case Type::POINT_N_COPY:
p = numPoint;
break;
@ -555,6 +595,18 @@ ExprVector EntityBase::PointGetExprs() const {
r = orig.Plus(trans);
break;
}
case Type::POINT_N_ROT_AXIS_TRANS: {
ExprVector orig = ExprVector::From(numPoint);
ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
ExprVector displace = ExprVector::From(param[4], param[5], param[6])
.WithMagnitude(Expr::From(1.0)).ScaledBy(Expr::From(timesApplied)).ScaledBy(Expr::From(param[7]));
ExprQuaternion q = GetAxisAngleQuaternionExprs(3);
orig = orig.Minus(trans);
orig = q.Rotate(orig);
r = orig.Plus(trans).Plus(displace);
break;
}
case Type::POINT_N_COPY:
r = ExprVector::From(numPoint);
break;
@ -565,7 +617,7 @@ ExprVector EntityBase::PointGetExprs() const {
}
void EntityBase::PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) const {
if(type == Type::POINT_IN_2D && workplane.v == wrkpl.v) {
if(type == Type::POINT_IN_2D && workplane == wrkpl) {
// They want our coordinates in the form that we've written them,
// very nice.
*u = Expr::From(param[0]);
@ -587,7 +639,7 @@ void EntityBase::PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) con
}
ExprVector EntityBase::PointGetExprsInWorkplane(hEntity wrkpl) const {
if(wrkpl.v == Entity::FREE_IN_3D.v) {
if(wrkpl == Entity::FREE_IN_3D) {
return PointGetExprs();
}
@ -633,7 +685,7 @@ ExprQuaternion EntityBase::GetAxisAngleQuaternionExprs(int param0) const {
Quaternion EntityBase::PointGetQuaternion() const {
Quaternion q;
if(type == Type::POINT_N_ROT_AA) {
if(type == Type::POINT_N_ROT_AA || type == Type::POINT_N_ROT_AXIS_TRANS) {
q = GetAxisAngleQuaternion(3);
} else if(type == Type::POINT_N_ROT_TRANS) {
q = Quaternion::From(param[3], param[4], param[5], param[6]);
@ -870,19 +922,16 @@ void EntityBase::GenerateEquations(IdList<Equation,hEquation> *l) const {
// If the two endpoints of the arc are constrained coincident
// (to make a complete circle), then our distance constraint
// would be redundant and therefore overconstrain things.
int i;
for(i = 0; i < SK.constraint.n; i++) {
ConstraintBase *c = &(SK.constraint.elem[i]);
if(c->group.v != group.v) continue;
if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
if((c->ptA.v == point[1].v && c->ptB.v == point[2].v) ||
(c->ptA.v == point[2].v && c->ptB.v == point[1].v))
{
break;
}
auto it = std::find_if(SK.constraint.begin(), SK.constraint.end(),
[&](ConstraintBase const &con) {
return (con.group == group) &&
(con.type == Constraint::Type::POINTS_COINCIDENT) &&
((con.ptA == point[1] && con.ptB == point[2]) ||
(con.ptA == point[2] && con.ptB == point[1]));
});
if(it != SK.constraint.end()) {
break;
}
if(i < SK.constraint.n) break;
Expr *ra = Constraint::Distance(workplane, point[0], point[1]);
Expr *rb = Constraint::Distance(workplane, point[0], point[2]);

View File

@ -28,7 +28,7 @@ void SolveSpaceUI::ExportSectionTo(const Platform::Path &filename) {
SS.GW.GroupSelection();
auto const &gs = SS.GW.gs;
if((gs.n == 0 && g->activeWorkplane.v != Entity::FREE_IN_3D.v)) {
if((gs.n == 0 && g->activeWorkplane != Entity::FREE_IN_3D)) {
Entity *wrkpl = SK.GetEntity(g->activeWorkplane);
origin = wrkpl->WorkplaneGetOffset();
n = wrkpl->Normal()->NormalN();
@ -96,9 +96,8 @@ void SolveSpaceUI::ExportSectionTo(const Platform::Path &filename) {
bl.CullIdenticalBeziers(/*both=*/true);
// Collect lines and beziers with custom style & export.
int i;
for(i = 0; i < SK.entity.n; i++) {
Entity *e = &(SK.entity.elem[i]);
for(auto &ent : SK.entity) {
Entity *e = &ent;
if (!e->IsVisible()) continue;
if (e->style.v < Style::FIRST_CUSTOM) continue;
if (!Style::Exportable(e->style.v)) continue;
@ -186,7 +185,6 @@ public:
};
void SolveSpaceUI::ExportViewOrWireframeTo(const Platform::Path &filename, bool exportWireframe) {
int i;
SEdgeList edges = {};
SBezierList beziers = {};
@ -206,8 +204,8 @@ void SolveSpaceUI::ExportViewOrWireframeTo(const Platform::Path &filename, bool
sm = NULL;
}
for(i = 0; i < SK.entity.n; i++) {
Entity *e = &(SK.entity.elem[i]);
for(auto &entity : SK.entity) {
Entity *e = &entity;
if(!e->IsVisible()) continue;
if(e->construction) continue;
@ -321,16 +319,14 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s
// Project into the export plane; so when we're done, z doesn't matter,
// and x and y are what goes in the DXF.
SEdge *e;
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
for(SEdge *e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
// project into the specified csys, and apply export scale
(e->a) = e->a.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
(e->b) = e->b.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
}
SBezier *b;
if(sbl) {
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
for(SBezier *b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
*b = b->InPerspective(u, v, n, origin, cameraTan);
int i;
for(i = 0; i <= b->deg; i++) {
@ -459,7 +455,7 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s
// segments with zero-length projections.
sel->l.ClearTags();
for(int i = 0; i < sel->l.n; ++i) {
SEdge *sei = &sel->l.elem[i];
SEdge *sei = &sel->l[i];
hStyle hsi = { (uint32_t)sei->auxA };
Style *si = Style::Get(hsi);
if(sei->tag != 0) continue;
@ -476,7 +472,7 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s
}
for(int j = i + 1; j < sel->l.n; ++j) {
SEdge *sej = &sel->l.elem[j];
SEdge *sej = &sel->l[j];
if(sej->tag != 0) continue;
Vector *pAj = &sej->a;
@ -578,12 +574,13 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s
// We kept the line segments and Beziers separate until now; but put them
// all together, and also project everything into the xy plane, since not
// all export targets ignore the z component of the points.
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
ssassert(sbl != nullptr, "Adding line segments to beziers assumes bezier list is non-null.");
for(SEdge *e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
SBezier sb = SBezier::From(e->a, e->b);
sb.auxA = e->auxA;
sbl->l.Add(&sb);
}
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
for(SBezier *b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
for(int i = 0; i <= b->deg; i++) {
b->ctrl[i].z = 0;
}
@ -762,9 +759,9 @@ void VectorFileWriter::OutputLinesAndMesh(SBezierLoopSetSet *sblss, SMesh *sm) {
void VectorFileWriter::BezierAsPwl(SBezier *sb) {
List<Vector> lv = {};
sb->MakePwlInto(&lv, SS.ExportChordTolMm());
int i;
for(i = 1; i < lv.n; i++) {
SBezier sb = SBezier::From(lv.elem[i-1], lv.elem[i]);
for(int i = 1; i < lv.n; i++) {
SBezier sb = SBezier::From(lv[i-1], lv[i]);
Bezier(&sb);
}
lv.Clear();
@ -849,6 +846,8 @@ void SolveSpaceUI::ExportMeshTo(const Platform::Path &filename) {
filename.HasExtension("html")) {
SOutlineList *e = &(SK.GetGroup(SS.GW.activeGroup)->displayOutlines);
ExportMeshAsThreeJsTo(f, filename, m, e);
} else if(filename.HasExtension("wrl")) {
ExportMeshAsVrmlTo(f, filename, m);
} else {
Error("Can't identify output file type from file extension of "
"filename '%s'; try .stl, .obj, .js, .html.", filename.raw.c_str());
@ -876,7 +875,7 @@ void SolveSpaceUI::ExportMeshAsStlTo(FILE *f, SMesh *sm) {
double s = SS.exportScale;
int i;
for(i = 0; i < sm->l.n; i++) {
STriangle *tr = &(sm->l.elem[i]);
STriangle *tr = &(sm->l[i]);
Vector n = tr->Normal().WithMagnitude(1);
float w;
w = (float)n.x; fwrite(&w, 4, 1, f);
@ -900,7 +899,7 @@ void SolveSpaceUI::ExportMeshAsStlTo(FILE *f, SMesh *sm) {
// Export the mesh as a Q3DO (https://github.com/q3k/q3d) file.
//-----------------------------------------------------------------------------
#include "object_generated.h"
#include "q3d_object_generated.h"
void SolveSpaceUI::ExportMeshAsQ3doTo(FILE *f, SMesh *sm) {
flatbuffers::FlatBufferBuilder builder(1024);
double s = SS.exportScale;
@ -982,7 +981,7 @@ void SolveSpaceUI::ExportMeshAsObjTo(FILE *fObj, FILE *fMtl, SMesh *sm) {
RgbaColor currentColor = {};
for(int i = 0; i < sm->l.n; i++) {
const STriangle &t = sm->l.elem[i];
const STriangle &t = sm->l[i];
if(!currentColor.Equals(t.meta.color)) {
currentColor = t.meta.color;
fprintf(fObj, "usemtl %s\n", colors[currentColor].c_str());
@ -1168,6 +1167,139 @@ void SolveSpaceUI::ExportMeshAsThreeJsTo(FILE *f, const Platform::Path &filename
spl.Clear();
}
//-----------------------------------------------------------------------------
// Export the mesh as a VRML text file / WRL.
//-----------------------------------------------------------------------------
void SolveSpaceUI::ExportMeshAsVrmlTo(FILE *f, const Platform::Path &filename, SMesh *sm) {
struct STriangleSpan {
STriangle *first, *past_last;
STriangle *begin() const { return first; }
STriangle *end() const { return past_last; }
};
std::string basename = filename.FileStem();
for(auto & c : basename) {
if(!(isalnum(c) || ((unsigned)c >= 0x80))) {
c = '_';
}
}
fprintf(f, "#VRML V2.0 utf8\n"
"#Exported from SolveSpace %s\n"
"\n"
"DEF %s Transform {\n"
" children [",
PACKAGE_VERSION,
basename.c_str());
std::map<std::uint8_t, std::vector<STriangleSpan>> opacities;
STriangle *start = sm->l.begin();
std::uint8_t last_opacity = start->meta.color.alpha;
for(auto & tr : sm->l) {
if(tr.meta.color.alpha != last_opacity) {
opacities[last_opacity].push_back(STriangleSpan{start, &tr});
start = &tr;
last_opacity = start->meta.color.alpha;
}
}
opacities[last_opacity].push_back(STriangleSpan{start, sm->l.end()});
for(auto && op : opacities) {
fprintf(f, "\n"
" Shape {\n"
" appearance Appearance {\n"
" material DEF %s_material_%u Material {\n"
" diffuseColor %f %f %f\n"
" ambientIntensity %f\n"
" transparency %f\n"
" }\n"
" }\n"
" geometry IndexedFaceSet {\n"
" colorPerVertex TRUE\n"
" coord Coordinate { point [\n",
basename.c_str(),
(unsigned)op.first,
SS.ambientIntensity,
SS.ambientIntensity,
SS.ambientIntensity,
SS.ambientIntensity,
1.f - ((float)op.first / 255.0f));
SPointList spl = {};
for(const auto & sp : op.second) {
for(const auto & tr : sp) {
spl.IncrementTagFor(tr.a);
spl.IncrementTagFor(tr.b);
spl.IncrementTagFor(tr.c);
}
}
// Output all the vertices.
for(auto sp : spl.l) {
fprintf(f, " %f %f %f,\n",
sp.p.x / SS.exportScale,
sp.p.y / SS.exportScale,
sp.p.z / SS.exportScale);
}
fputs(" ] }\n"
" coordIndex [\n", f);
// And now all the triangular faces, in terms of those vertices.
for(const auto & sp : op.second) {
for(const auto & tr : sp) {
fprintf(f, " %d, %d, %d, -1,\n",
spl.IndexForPoint(tr.a),
spl.IndexForPoint(tr.b),
spl.IndexForPoint(tr.c));
}
}
fputs(" ]\n"
" color Color { color [\n", f);
// Output triangle colors.
std::vector<int> triangle_colour_ids;
std::vector<RgbaColor> colours_present;
for(const auto & sp : op.second) {
for(const auto & tr : sp) {
const auto colour_itr = std::find_if(colours_present.begin(), colours_present.end(),
[&](const RgbaColor & c) {
return c.Equals(tr.meta.color);
});
if(colour_itr == colours_present.end()) {
fprintf(f, " %.10f %.10f %.10f,\n",
tr.meta.color.redF(),
tr.meta.color.greenF(),
tr.meta.color.blueF());
triangle_colour_ids.push_back(colours_present.size());
colours_present.insert(colours_present.end(), tr.meta.color);
} else {
triangle_colour_ids.push_back(colour_itr - colours_present.begin());
}
}
}
fputs(" ] }\n"
" colorIndex [\n", f);
for(auto colour_idx : triangle_colour_ids) {
fprintf(f, " %d, %d, %d, -1,\n", colour_idx, colour_idx, colour_idx);
}
fputs(" ]\n"
" }\n"
" }\n", f);
spl.Clear();
}
fputs(" ]\n"
"}\n", f);
}
//-----------------------------------------------------------------------------
// Export a view of the model as an image; we just take a screenshot, by
// rendering the view in the usual way and then copying the pixels.

View File

@ -121,7 +121,7 @@ int StepFileWriter::ExportCurveLoop(SBezierLoop *loop, bool inner) {
List<int> listOfTrims = {};
SBezier *sb = &(loop->l.elem[loop->l.n - 1]);
SBezier *sb = loop->l.Last();
// Generate "exactly closed" contours, with the same vertex id for the
// finish of a previous edge and the start of the next one. So we need
@ -296,12 +296,13 @@ void StepFileWriter::ExportSurfacesTo(const Platform::Path &filename) {
Group *g = SK.GetGroup(SS.GW.activeGroup);
SShell *shell = &(g->runningShell);
if(shell->surface.n == 0) {
if(shell->surface.IsEmpty()) {
Error("The model does not contain any surfaces to export.%s",
g->runningMesh.l.n > 0 ?
"\n\nThe model does contain triangles from a mesh, but "
"a triangle mesh cannot be exported as a STEP file. Try "
"File -> Export Mesh... instead." : "");
!g->runningMesh.l.IsEmpty()
? "\n\nThe model does contain triangles from a mesh, but "
"a triangle mesh cannot be exported as a STEP file. Try "
"File -> Export Mesh... instead."
: "");
return;
}
@ -318,7 +319,8 @@ void StepFileWriter::ExportSurfacesTo(const Platform::Path &filename) {
SSurface *ss;
for(ss = shell->surface.First(); ss; ss = shell->surface.NextAfter(ss)) {
if(ss->trim.n == 0) continue;
if(ss->trim.IsEmpty())
continue;
// Get all of the loops of Beziers that trim our surface (with each
// Bezier split so that we use the section as t goes from 0 to 1), and

View File

@ -325,7 +325,7 @@ public:
void assignEntityDefaults(DRW_Entity *entity, hStyle hs) {
Style *s = Style::Get(hs);
RgbaColor color = s->Color(hs, /*forExport=*/true);
RgbaColor color = Style::Color(hs, /*forExport=*/true);
entity->color24 = color.ToPackedIntBGRA();
entity->color = findDxfColor(color);
entity->layer = s->DescriptionString();
@ -370,7 +370,7 @@ public:
DRW_Polyline polyline;
assignEntityDefaults(&polyline, hs);
for(int i = 0; i < lv.n; i++) {
Vector *v = &lv.elem[i];
Vector *v = &lv[i];
DRW_Vertex *vertex = new DRW_Vertex(v->x, v->y, v->z, 0.0);
polyline.vertlist.push_back(vertex);
}
@ -1027,8 +1027,9 @@ void SvgFileWriter::StartFile() {
double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000;
fprintf(f, "stroke-width:%f;\r\n", sw);
fprintf(f, "}\r\n");
for(int i = 0; i < SK.style.n; i++) {
Style *s = &SK.style.elem[i];
for(auto &style : SK.style) {
Style *s = &style;
RgbaColor strokeRgb = Style::Color(s->h, /*forExport=*/true);
StipplePattern pattern = Style::PatternType(s->h);
double stippleScale = Style::StippleScaleMm(s->h);

View File

@ -361,8 +361,8 @@ Expr *Expr::PartialWrt(hParam p) const {
Expr *da, *db;
switch(op) {
case Op::PARAM_PTR: return From(p.v == parp->h.v ? 1 : 0);
case Op::PARAM: return From(p.v == parh.v ? 1 : 0);
case Op::PARAM_PTR: return From(p == parp->h ? 1 : 0);
case Op::PARAM: return From(p == parh ? 1 : 0);
case Op::CONSTANT: return From(0.0);
case Op::VARIABLE: ssassert(false, "Not supported yet");
@ -412,8 +412,8 @@ uint64_t Expr::ParamsUsed() const {
}
bool Expr::DependsOn(hParam p) const {
if(op == Op::PARAM) return (parh.v == p.v);
if(op == Op::PARAM_PTR) return (parp->h.v == p.v);
if(op == Op::PARAM) return (parh == p);
if(op == Op::PARAM_PTR) return (parp->h == p);
int c = Children();
if(c == 1) return a->DependsOn(p);
@ -494,7 +494,7 @@ Expr *Expr::FoldConstants() {
void Expr::Substitute(hParam oldh, hParam newh) {
ssassert(op != Op::PARAM_PTR, "Expected an expression that refer to params via handles");
if(op == Op::PARAM && parh.v == oldh.v) {
if(op == Op::PARAM && parh == oldh) {
parh = newh;
}
int c = Children();
@ -528,11 +528,11 @@ hParam Expr::ReferencedParams(ParamList *pl) const {
hParam pa, pb;
pa = a->ReferencedParams(pl);
pb = b->ReferencedParams(pl);
if(pa.v == NO_PARAMS.v) {
if(pa == NO_PARAMS) {
return pb;
} else if(pb.v == NO_PARAMS.v) {
} else if(pb == NO_PARAMS) {
return pa;
} else if(pa.v == pb.v) {
} else if(pa == pb) {
return pa; // either, doesn't matter
} else {
return MULTIPLE_PARAMS;

View File

@ -45,7 +45,7 @@ public:
Expr *b;
};
Expr() { }
Expr() = default;
Expr(double val) : op(Op::CONSTANT) { v = val; }
static inline Expr *AllocExpr()

View File

@ -20,8 +20,8 @@ void SolveSpaceUI::ClearExisting() {
UndoClearStack(&redo);
UndoClearStack(&undo);
for(int i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
for(hGroup hg : SK.groupOrder) {
Group *g = SK.GetGroup(hg);
g->Clear();
}
@ -301,39 +301,39 @@ bool SolveSpaceUI::SaveToFile(const Platform::Path &filename) {
fprintf(fh, "%s\n\n\n", VERSION_STRING);
int i, j;
for(i = 0; i < SK.group.n; i++) {
sv.g = SK.group.elem[i];
for(auto &g : SK.group) {
sv.g = g;
SaveUsingTable(filename, 'g');
fprintf(fh, "AddGroup\n\n");
}
for(i = 0; i < SK.param.n; i++) {
sv.p = SK.param.elem[i];
for(auto &p : SK.param) {
sv.p = p;
SaveUsingTable(filename, 'p');
fprintf(fh, "AddParam\n\n");
}
for(i = 0; i < SK.request.n; i++) {
sv.r = SK.request.elem[i];
for(auto &r : SK.request) {
sv.r = r;
SaveUsingTable(filename, 'r');
fprintf(fh, "AddRequest\n\n");
}
for(i = 0; i < SK.entity.n; i++) {
(SK.entity.elem[i]).CalculateNumerical(/*forExport=*/true);
sv.e = SK.entity.elem[i];
for(auto &e : SK.entity) {
e.CalculateNumerical(/*forExport=*/true);
sv.e = e;
SaveUsingTable(filename, 'e');
fprintf(fh, "AddEntity\n\n");
}
for(i = 0; i < SK.constraint.n; i++) {
sv.c = SK.constraint.elem[i];
for(auto &c : SK.constraint) {
sv.c = c;
SaveUsingTable(filename, 'c');
fprintf(fh, "AddConstraint\n\n");
}
for(i = 0; i < SK.style.n; i++) {
sv.s = SK.style.elem[i];
for(auto &s : SK.style) {
sv.s = s;
if(sv.s.h.v >= Style::FIRST_CUSTOM) {
SaveUsingTable(filename, 's');
fprintf(fh, "AddStyle\n\n");
@ -343,10 +343,10 @@ bool SolveSpaceUI::SaveToFile(const Platform::Path &filename) {
// A group will have either a mesh or a shell, but not both; but the code
// to print either of those just does nothing if the mesh/shell is empty.
Group *g = SK.GetGroup(SK.groupOrder.elem[SK.groupOrder.n - 1]);
Group *g = SK.GetGroup(*SK.groupOrder.Last());
SMesh *m = &g->runningMesh;
for(i = 0; i < m->l.n; i++) {
STriangle *tr = &(m->l.elem[i]);
STriangle *tr = &(m->l[i]);
fprintf(fh, "Triangle %08x %08x "
"%.20f %.20f %.20f %.20f %.20f %.20f %.20f %.20f %.20f\n",
tr->meta.face, tr->meta.color.ToPackedInt(),
@ -436,8 +436,17 @@ void SolveSpaceUI::LoadUsingTable(const Platform::Path &filename, char *key, cha
if (fgets(line2, (int)sizeof(line2), fh) == NULL)
break;
if(sscanf(line2, "%d %x %d", &(ei.v), &(ek.input.v),
&(ek.copyNumber)) == 3)
{
&(ek.copyNumber)) == 3) {
if(ei.v == Entity::NO_ENTITY.v) {
// Commit bd84bc1a mistakenly introduced code that would remap
// some entities to NO_ENTITY. This was fixed in commit bd84bc1a,
// but files created meanwhile are corrupt, and can cause crashes.
//
// To fix this, we skip any such remaps when loading; they will be
// recreated on the next regeneration. Any resulting orphans will
// be pruned in the usual way, recovering to a well-defined state.
continue;
}
p->M().insert({ ek, ei });
} else {
break;
@ -540,7 +549,7 @@ bool SolveSpaceUI::LoadFromFile(const Platform::Path &filename, bool canCancel)
Error(_("Unrecognized data in file. This file may be corrupt, or "
"from a newer version of the program."));
// At least leave the program in a non-crashing state.
if(SK.group.n == 0) {
if(SK.group.IsEmpty()) {
NewFile();
}
}

View File

@ -14,11 +14,10 @@ void SolveSpaceUI::MarkGroupDirtyByEntity(hEntity he) {
}
void SolveSpaceUI::MarkGroupDirty(hGroup hg, bool onlyThis) {
int i;
bool go = false;
for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
if(g->h.v == hg.v) {
for(auto const &gh : SK.groupOrder) {
Group *g = SK.GetGroup(gh);
if(g->h == hg) {
go = true;
}
if(go) {
@ -31,23 +30,20 @@ void SolveSpaceUI::MarkGroupDirty(hGroup hg, bool onlyThis) {
}
bool SolveSpaceUI::PruneOrphans() {
int i;
for(i = 0; i < SK.request.n; i++) {
Request *r = &(SK.request.elem[i]);
if(GroupExists(r->group)) continue;
auto r = std::find_if(SK.request.begin(), SK.request.end(),
[&](Request &r) { return !GroupExists(r.group); });
if(r != SK.request.end()) {
(deleted.requests)++;
SK.request.RemoveById(r->h);
return true;
}
for(i = 0; i < SK.constraint.n; i++) {
Constraint *c = &(SK.constraint.elem[i]);
if(GroupExists(c->group)) continue;
auto c = std::find_if(SK.constraint.begin(), SK.constraint.end(),
[&](Constraint &c) { return !GroupExists(c.group); });
if(c != SK.constraint.end()) {
(deleted.constraints)++;
(deleted.nonTrivialConstraints)++;
SK.constraint.RemoveById(c->h);
return true;
}
@ -72,7 +68,7 @@ bool SolveSpaceUI::GroupExists(hGroup hg) {
bool SolveSpaceUI::EntityExists(hEntity he) {
// A nonexstient entity is acceptable, though, usually just means it
// doesn't apply.
if(he.v == Entity::NO_ENTITY.v) return true;
if(he == Entity::NO_ENTITY) return true;
return SK.entity.FindByIdNoOops(he) ? true : false;
}
@ -91,44 +87,38 @@ bool SolveSpaceUI::PruneGroups(hGroup hg) {
}
bool SolveSpaceUI::PruneRequests(hGroup hg) {
int i;
for(i = 0; i < SK.entity.n; i++) {
Entity *e = &(SK.entity.elem[i]);
if(e->group.v != hg.v) continue;
if(EntityExists(e->workplane)) continue;
ssassert(e->h.isFromRequest(), "Only explicitly created entities can be pruned");
auto e = std::find_if(SK.entity.begin(), SK.entity.end(),
[&](Entity &e) { return e.group == hg && !EntityExists(e.workplane); });
if(e != SK.entity.end()) {
(deleted.requests)++;
SK.request.RemoveById(e->h.request());
SK.entity.RemoveById(e->h);
return true;
}
return false;
}
bool SolveSpaceUI::PruneConstraints(hGroup hg) {
int i;
for(i = 0; i < SK.constraint.n; i++) {
Constraint *c = &(SK.constraint.elem[i]);
if(c->group.v != hg.v) continue;
auto c = std::find_if(SK.constraint.begin(), SK.constraint.end(), [&](Constraint &c) {
if(c.group != hg)
return false;
if(EntityExists(c->workplane) &&
EntityExists(c->ptA) &&
EntityExists(c->ptB) &&
EntityExists(c->entityA) &&
EntityExists(c->entityB) &&
EntityExists(c->entityC) &&
EntityExists(c->entityD))
{
continue;
if(EntityExists(c.workplane) &&
EntityExists(c.ptA) &&
EntityExists(c.ptB) &&
EntityExists(c.entityA) &&
EntityExists(c.entityB) &&
EntityExists(c.entityC) &&
EntityExists(c.entityD)) {
return false;
}
return true;
});
if(c != SK.constraint.end()) {
(deleted.constraints)++;
if(c->type != Constraint::Type::POINTS_COINCIDENT &&
c->type != Constraint::Type::HORIZONTAL &&
c->type != Constraint::Type::VERTICAL)
{
c->type != Constraint::Type::VERTICAL) {
(deleted.nonTrivialConstraints)++;
}
@ -139,14 +129,13 @@ bool SolveSpaceUI::PruneConstraints(hGroup hg) {
}
void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox) {
int first = 0, last = 0, i, j;
int first = 0, last = 0, i;
uint64_t startMillis = GetMilliseconds(),
endMillis;
SK.groupOrder.Clear();
for(int i = 0; i < SK.group.n; i++)
SK.groupOrder.Add(&SK.group.elem[i].h);
for(auto &g : SK.group) { SK.groupOrder.Add(&g.h); }
std::sort(SK.groupOrder.begin(), SK.groupOrder.end(),
[](const hGroup &ha, const hGroup &hb) {
return SK.GetGroup(ha)->order < SK.GetGroup(hb)->order;
@ -159,12 +148,13 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
// Start from the first dirty group, and solve until the active group,
// since all groups after the active group are hidden.
// Not using range-for because we're tracking the indices.
for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
Group *g = SK.GetGroup(SK.groupOrder[i]);
if((!g->clean) || !g->IsSolvedOkay()) {
first = min(first, i);
}
if(g->h.v == SS.GW.activeGroup.v) {
if(g->h == SS.GW.activeGroup) {
last = i;
}
}
@ -190,7 +180,7 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
case Generate::UNTIL_ACTIVE: {
for(i = 0; i < SK.groupOrder.n; i++) {
if(SK.groupOrder.elem[i].v == SS.GW.activeGroup.v)
if(SK.groupOrder[i] == SS.GW.activeGroup)
break;
}
@ -224,8 +214,9 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
SK.entity.Clear();
SK.entity.ReserveMore(oldEntityCount);
// Not using range-for because we're using the index inside the loop.
for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
Group *g = SK.GetGroup(SK.groupOrder[i]);
// The group may depend on entities or other groups, to define its
// workplane geometry or for its operands. Those must already exist
@ -233,15 +224,15 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
if(PruneGroups(g->h))
goto pruned;
for(j = 0; j < SK.request.n; j++) {
Request *r = &(SK.request.elem[j]);
if(r->group.v != g->h.v) continue;
for(auto &req : SK.request) {
Request *r = &req;
if(r->group != g->h) continue;
r->Generate(&(SK.entity), &(SK.param));
}
for(j = 0; j < SK.constraint.n; j++) {
Constraint *c = &SK.constraint.elem[j];
if(c->group.v != g->h.v) continue;
for(auto &con : SK.constraint) {
Constraint *c = &con;
if(c->group != g->h) continue;
c->Generate(&(SK.param));
}
@ -254,8 +245,8 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
// Use the previous values for params that we've seen before, as
// initial guesses for the solver.
for(j = 0; j < SK.param.n; j++) {
Param *newp = &(SK.param.elem[j]);
for(auto &p : SK.param) {
Param *newp = &p;
if(newp->known) continue;
Param *prevp = prev.FindByIdNoOops(newp->h);
@ -265,11 +256,12 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
}
}
if(g->h.v == Group::HGROUP_REFERENCES.v) {
if(g->h == Group::HGROUP_REFERENCES) {
ForceReferences();
g->solved.how = SolveResult::OKAY;
g->clean = true;
} else {
// this i is an index in groupOrder
if(i >= first && i <= last) {
// The group falls inside the range, so really solve it,
// and then regenerate the mesh based on the solved stuff.
@ -284,8 +276,8 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
// The group falls outside the range, so just assume that
// it's good wherever we left it. The mesh is unchanged,
// and the parameters must be marked as known.
for(j = 0; j < SK.param.n; j++) {
Param *newp = &(SK.param.elem[j]);
for(auto &p : SK.param) {
Param *newp = &p;
Param *prevp = prev.FindByIdNoOops(newp->h);
if(prevp) newp->known = true;
@ -295,8 +287,8 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
}
// And update any reference dimensions with their new values
for(i = 0; i < SK.constraint.n; i++) {
Constraint *c = &(SK.constraint.elem[i]);
for(auto &con : SK.constraint) {
Constraint *c = &con;
if(c->reference) {
c->ModifyToSatisfy();
}
@ -433,7 +425,7 @@ void SolveSpaceUI::MarkDraggedParams() {
if(i == -1) {
hp = SS.GW.pending.point;
} else {
hp = SS.GW.pending.points.elem[i];
hp = SS.GW.pending.points[i];
}
if(!hp.v) continue;
@ -445,6 +437,7 @@ void SolveSpaceUI::MarkDraggedParams() {
switch(pt->type) {
case Entity::Type::POINT_N_TRANS:
case Entity::Type::POINT_IN_3D:
case Entity::Type::POINT_N_ROT_AXIS_TRANS:
sys.dragged.Add(&(pt->param[0]));
sys.dragged.Add(&(pt->param[1]));
sys.dragged.Add(&(pt->param[2]));
@ -504,21 +497,20 @@ void SolveSpaceUI::SolveGroupAndReport(hGroup hg, bool andFindFree) {
}
void SolveSpaceUI::WriteEqSystemForGroup(hGroup hg) {
int i;
// Clear out the system to be solved.
sys.entity.Clear();
sys.param.Clear();
sys.eq.Clear();
// And generate all the params for requests in this group
for(i = 0; i < SK.request.n; i++) {
Request *r = &(SK.request.elem[i]);
if(r->group.v != hg.v) continue;
for(auto &req : SK.request) {
Request *r = &req;
if(r->group != hg) continue;
r->Generate(&(sys.entity), &(sys.param));
}
for(i = 0; i < SK.constraint.n; i++) {
Constraint *c = &SK.constraint.elem[i];
if(c->group.v != hg.v) continue;
for(auto &con : SK.constraint) {
Constraint *c = &con;
if(c->group != hg) continue;
c->Generate(&(sys.param));
}
@ -526,8 +518,8 @@ void SolveSpaceUI::WriteEqSystemForGroup(hGroup hg) {
Group *g = SK.GetGroup(hg);
g->Generate(&(sys.entity), &(sys.param));
// Set the initial guesses for all the params
for(i = 0; i < sys.param.n; i++) {
Param *p = &(sys.param.elem[i]);
for(auto &param : sys.param) {
Param *p = &param;
p->known = false;
p->val = SK.GetParam(p->h)->val;
}
@ -562,10 +554,10 @@ SolveResult SolveSpaceUI::TestRankForGroup(hGroup hg, int *rank) {
bool SolveSpaceUI::ActiveGroupsOkay() {
for(int i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
Group *g = SK.GetGroup(SK.groupOrder[i]);
if(!g->IsSolvedOkay())
return false;
if(g->h.v == SS.GW.activeGroup.v)
if(g->h == SS.GW.activeGroup)
break;
}
return true;

View File

@ -105,7 +105,9 @@ const MenuEntry Menu[] = {
{ 1, N_("Step &Rotating"), Command::GROUP_ROT, S|'r', KN, mGrp },
{ 1, NULL, Command::NONE, 0, KN, NULL },
{ 1, N_("E&xtrude"), Command::GROUP_EXTRUDE, S|'x', KN, mGrp },
{ 1, N_("&Helix"), Command::GROUP_HELIX, S|'h', KN, mGrp },
{ 1, N_("&Lathe"), Command::GROUP_LATHE, S|'l', KN, mGrp },
{ 1, N_("Re&volve"), Command::GROUP_REVOLVE, S|'v', KN, mGrp },
{ 1, NULL, Command::NONE, 0, KN, NULL },
{ 1, N_("Link / Assemble..."), Command::GROUP_LINK, S|'i', KN, mGrp },
{ 1, N_("Link Recent"), Command::GROUP_RECENT, 0, KN, mGrp },
@ -379,7 +381,9 @@ void GraphicsWindow::Init() {
orig.projUp = projUp;
// And with the last group active
activeGroup = SK.groupOrder.elem[SK.groupOrder.n - 1];
ssassert(!SK.groupOrder.IsEmpty(),
"Group order can't be empty since we will activate the last group.");
activeGroup = *SK.groupOrder.Last();
SK.GetGroup(activeGroup)->Activate();
showWorkplanes = false;
@ -571,7 +575,7 @@ void GraphicsWindow::LoopOverPoints(const std::vector<Entity *> &entities,
Group *g = SK.GetGroup(activeGroup);
g->GenerateDisplayItems();
for(int i = 0; i < g->displayMesh.l.n; i++) {
STriangle *tr = &(g->displayMesh.l.elem[i]);
STriangle *tr = &(g->displayMesh.l[i]);
if(!includeMesh) {
bool found = false;
for(const hEntity &face : faces) {
@ -587,9 +591,9 @@ void GraphicsWindow::LoopOverPoints(const std::vector<Entity *> &entities,
}
if(!includeMesh) return;
for(int i = 0; i < g->polyLoops.l.n; i++) {
SContour *sc = &(g->polyLoops.l.elem[i]);
SContour *sc = &(g->polyLoops.l[i]);
for(int j = 0; j < sc->l.n; j++) {
HandlePointForZoomToFit(sc->l.elem[j].p, pmax, pmin, wmin, usePerspective, camera);
HandlePointForZoomToFit(sc->l[j].p, pmax, pmin, wmin, usePerspective, camera);
}
}
}
@ -606,7 +610,7 @@ double GraphicsWindow::ZoomToFit(const Camera &camera,
if(useSelection) {
for(int i = 0; i < selection.n; i++) {
Selection *s = &selection.elem[i];
Selection *s = &selection[i];
if(s->entity.v != 0) {
Entity *e = SK.entity.FindById(s->entity);
if(e->IsFace()) {
@ -844,10 +848,11 @@ void GraphicsWindow::EnsureValidActives() {
bool change = false;
// The active group must exist, and not be the references.
Group *g = SK.group.FindByIdNoOops(activeGroup);
if((!g) || (g->h.v == Group::HGROUP_REFERENCES.v)) {
if((!g) || (g->h == Group::HGROUP_REFERENCES)) {
// Not using range-for because this is used to find an index.
int i;
for(i = 0; i < SK.groupOrder.n; i++) {
if(SK.groupOrder.elem[i].v != Group::HGROUP_REFERENCES.v) {
if(SK.groupOrder[i] != Group::HGROUP_REFERENCES) {
break;
}
}
@ -863,7 +868,7 @@ void GraphicsWindow::EnsureValidActives() {
// do it now so that drawing mode isn't switched to "Free in 3d".
SS.GenerateAll(SolveSpaceUI::Generate::ALL);
} else {
activeGroup = SK.groupOrder.elem[i];
activeGroup = SK.groupOrder[i];
}
SK.GetGroup(activeGroup)->Activate();
change = true;
@ -874,7 +879,7 @@ void GraphicsWindow::EnsureValidActives() {
Entity *e = SK.entity.FindByIdNoOops(ActiveWorkplane());
if(e) {
hGroup hgw = e->group;
if(hgw.v != activeGroup.v && SS.GroupsInOrder(activeGroup, hgw)) {
if(hgw != activeGroup && SS.GroupsInOrder(activeGroup, hgw)) {
// The active workplane is in a group that comes after the
// active group; so any request or constraint will fail.
SetWorkplaneFreeIn3d();
@ -931,7 +936,7 @@ hEntity GraphicsWindow::ActiveWorkplane() {
}
}
bool GraphicsWindow::LockedInWorkplane() {
return (SS.GW.ActiveWorkplane().v != Entity::FREE_IN_3D.v);
return (SS.GW.ActiveWorkplane() != Entity::FREE_IN_3D);
}
void GraphicsWindow::ForceTextWindowShown() {
@ -1026,7 +1031,7 @@ void GraphicsWindow::MenuEdit(Command id) {
case Command::SELECT_ALL: {
Entity *e;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
if(e->group.v != SS.GW.activeGroup.v) continue;
if(e->group != SS.GW.activeGroup) continue;
if(e->IsFace() || e->IsDistance()) continue;
if(!e->IsVisible()) continue;
@ -1044,7 +1049,7 @@ void GraphicsWindow::MenuEdit(Command id) {
do {
didSomething = false;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
if(e->group.v != SS.GW.activeGroup.v) continue;
if(e->group != SS.GW.activeGroup) continue;
if(!e->HasEndpoints()) continue;
if(!e->IsVisible()) continue;
@ -1055,7 +1060,7 @@ void GraphicsWindow::MenuEdit(Command id) {
List<Selection> *ls = &(SS.GW.selection);
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
if(!s->entity.v) continue;
if(s->entity.v == e->h.v) {
if(s->entity == e->h) {
alreadySelected = true;
continue;
}
@ -1302,7 +1307,7 @@ void GraphicsWindow::ToggleBool(bool *v) {
// If the mesh or edges were previously hidden, they haven't been generated,
// and if we are going to show them, we need to generate them first.
Group *g = SK.GetGroup(SS.GW.activeGroup);
if(*v && (g->displayOutlines.l.n == 0 && (v == &showEdges || v == &showOutlines))) {
if(*v && (g->displayOutlines.l.IsEmpty() && (v == &showEdges || v == &showOutlines))) {
SS.GenerateAll(SolveSpaceUI::Generate::UNTIL_ACTIVE);
}

View File

@ -49,8 +49,8 @@ bool Group::IsVisible() {
return true;
}
size_t Group::GetNumConstraints(void) {
return SK.constraint.CountIf([&](Constraint const & c) { return c.group.v == h.v; });
size_t Group::GetNumConstraints() {
return SK.constraint.CountIf([&](Constraint const & c) { return c.group == h; });
}
Vector Group::ExtrusionGetVector() {
@ -163,6 +163,10 @@ void Group::MenuGroup(Command id, Platform::Path linkFile) {
break;
case Command::GROUP_LATHE:
if(!SS.GW.LockedInWorkplane()) {
Error(_("Lathe operation can only be applied to planar sketches."));
return;
}
if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) {
g.predef.origin = gs.point[0];
g.predef.entityB = gs.vector[0];
@ -184,6 +188,62 @@ void Group::MenuGroup(Command id, Platform::Path linkFile) {
g.name = C_("group-name", "lathe");
break;
case Command::GROUP_REVOLVE:
if(!SS.GW.LockedInWorkplane()) {
Error(_("Revolve operation can only be applied to planar sketches."));
return;
}
if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) {
g.predef.origin = gs.point[0];
g.predef.entityB = gs.vector[0];
} else if(gs.lineSegments == 1 && gs.n == 1) {
g.predef.origin = SK.GetEntity(gs.entity[0])->point[0];
g.predef.entityB = gs.entity[0];
// since a line segment is a vector
} else {
Error(_("Bad selection for new revolve group. This group can "
"be created with:\n\n"
" * a point and a line segment or normal "
"(revolved about an axis parallel to line / "
"normal, through point)\n"
" * a line segment (revolved about line segment)\n"));
return;
}
g.type = Type::REVOLVE;
g.opA = SS.GW.activeGroup;
g.valA = 2;
g.subtype = Subtype::ONE_SIDED;
g.name = C_("group-name", "revolve");
break;
case Command::GROUP_HELIX:
if(!SS.GW.LockedInWorkplane()) {
Error(_("Helix operation can only be applied to planar sketches."));
return;
}
if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) {
g.predef.origin = gs.point[0];
g.predef.entityB = gs.vector[0];
} else if(gs.lineSegments == 1 && gs.n == 1) {
g.predef.origin = SK.GetEntity(gs.entity[0])->point[0];
g.predef.entityB = gs.entity[0];
// since a line segment is a vector
} else {
Error(_("Bad selection for new helix group. This group can "
"be created with:\n\n"
" * a point and a line segment or normal "
"(revolved about an axis parallel to line / "
"normal, through point)\n"
" * a line segment (revolved about line segment)\n"));
return;
}
g.type = Type::HELIX;
g.opA = SS.GW.activeGroup;
g.valA = 2;
g.subtype = Subtype::ONE_SIDED;
g.name = C_("group-name", "helix");
break;
case Command::GROUP_ROT: {
if(gs.points == 1 && gs.n == 1 && SS.GW.LockedInWorkplane()) {
g.predef.origin = gs.point[0];
@ -249,7 +309,7 @@ void Group::MenuGroup(Command id, Platform::Path linkFile) {
}
// Copy color from the previous mesh-contributing group.
if(g.IsMeshGroup() && SK.groupOrder.n > 0) {
if(g.IsMeshGroup() && !SK.groupOrder.IsEmpty()) {
Group *running = SK.GetRunningMeshGroupFor(SS.GW.activeGroup);
if(running != NULL) {
g.color = running->color;
@ -260,11 +320,11 @@ void Group::MenuGroup(Command id, Platform::Path linkFile) {
SS.UndoRemember();
bool afterActive = false;
for(int i = 0; i < SK.groupOrder.n; i++) {
Group *gi = SK.GetGroup(SK.groupOrder.elem[i]);
for(hGroup hg : SK.groupOrder) {
Group *gi = SK.GetGroup(hg);
if(afterActive)
gi->order += 1;
if(gi->h.v == SS.GW.activeGroup.v) {
if(gi->h == SS.GW.activeGroup) {
g.order = gi->order + 1;
afterActive = true;
}
@ -344,7 +404,7 @@ std::string Group::DescriptionString() {
void Group::Activate() {
if(type == Type::EXTRUDE || type == Type::LINKED || type == Type::LATHE ||
type == Type::TRANSLATE || type == Type::ROTATE) {
type == Type::REVOLVE || type == Type::HELIX || type == Type::TRANSLATE || type == Type::ROTATE) {
SS.GW.showFaces = true;
} else {
SS.GW.showFaces = false;
@ -424,9 +484,10 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
// Get some arbitrary point in the sketch, that will be used
// as a reference when defining top and bottom faces.
hEntity pt = { 0 };
// Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) {
Entity *e = &(entity->elem[i]);
if(e->group.v != opA.v) continue;
Entity *e = &(entity->Get(i));
if(e->group != opA) continue;
if(e->IsPoint()) pt = e->h;
@ -436,11 +497,11 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
// adds entities, which may cause a realloc.
CopyEntity(entity, SK.GetEntity(he), ai, REMAP_BOTTOM,
h.param(0), h.param(1), h.param(2),
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
CopyAs::N_TRANS);
CopyEntity(entity, SK.GetEntity(he), af, REMAP_TOP,
h.param(0), h.param(1), h.param(2),
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
CopyAs::N_TRANS);
MakeExtrusionLines(entity, he);
}
@ -457,9 +518,10 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
// Remapped entity index.
int ai = 1;
// Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) {
Entity *e = &(entity->elem[i]);
if(e->group.v != opA.v) continue;
Entity *e = &(entity->Get(i));
if(e->group != opA) continue;
e->CalculateNumerical(/*forExport=*/false);
hEntity he = e->h;
@ -468,20 +530,133 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
// adds entities, which may cause a realloc.
CopyEntity(entity, SK.GetEntity(predef.origin), 0, ai,
NO_PARAM, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
CopyAs::NUMERIC);
CopyEntity(entity, SK.GetEntity(he), 0, REMAP_LATHE_START,
NO_PARAM, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
CopyAs::NUMERIC);
CopyEntity(entity, SK.GetEntity(he), 0, REMAP_LATHE_END,
NO_PARAM, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
CopyAs::NUMERIC);
MakeLatheCircles(entity, param, he, axis_pos, axis_dir, ai);
MakeLatheSurfacesSelectable(entity, he, axis_dir);
ai++;
}
return;
}
case Type::REVOLVE: {
// this was borrowed from LATHE and ROTATE
Vector axis_pos = SK.GetEntity(predef.origin)->PointGetNum();
Vector axis_dir = SK.GetEntity(predef.entityB)->VectorGetNum();
// The center of rotation
AddParam(param, h.param(0), axis_pos.x);
AddParam(param, h.param(1), axis_pos.y);
AddParam(param, h.param(2), axis_pos.z);
// The rotation quaternion
AddParam(param, h.param(3), 30 * PI / 180);
AddParam(param, h.param(4), axis_dir.x);
AddParam(param, h.param(5), axis_dir.y);
AddParam(param, h.param(6), axis_dir.z);
int ai = 1;
// Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) {
Entity *e = &(entity->Get(i));
if(e->group != opA)
continue;
e->CalculateNumerical(/*forExport=*/false);
hEntity he = e->h;
CopyEntity(entity, SK.GetEntity(predef.origin), 0, ai, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, CopyAs::NUMERIC);
for(a = 0; a < 2; a++) {
//! @todo is this check redundant?
Entity *e = &(entity->Get(i));
if(e->group != opA)
continue;
e->CalculateNumerical(false);
CopyEntity(entity, e, a * 2 - (subtype == Subtype::ONE_SIDED ? 0 : 1),
(a == 1) ? REMAP_LATHE_END : REMAP_LATHE_START, h.param(0),
h.param(1), h.param(2), h.param(3), h.param(4), h.param(5),
h.param(6), NO_PARAM, CopyAs::N_ROT_AA);
}
// Arcs are not generated for revolve groups, for now, because our current arc
// entity is not chiral, and dragging a revolve may break the arc by inverting it.
// MakeLatheCircles(entity, param, he, axis_pos, axis_dir, ai);
MakeLatheSurfacesSelectable(entity, he, axis_dir);
ai++;
}
return;
}
case Type::HELIX: {
Vector axis_pos = SK.GetEntity(predef.origin)->PointGetNum();
Vector axis_dir = SK.GetEntity(predef.entityB)->VectorGetNum();
// The center of rotation
AddParam(param, h.param(0), axis_pos.x);
AddParam(param, h.param(1), axis_pos.y);
AddParam(param, h.param(2), axis_pos.z);
// The rotation quaternion
AddParam(param, h.param(3), 30 * PI / 180);
AddParam(param, h.param(4), axis_dir.x);
AddParam(param, h.param(5), axis_dir.y);
AddParam(param, h.param(6), axis_dir.z);
// distance to translate along the rotation axis
AddParam(param, h.param(7), 20);
int ai = 1;
// Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) {
Entity *e = &(entity->Get(i));
if((e->group.v != opA.v) && !(e->h == predef.origin))
continue;
e->CalculateNumerical(/*forExport=*/false);
CopyEntity(entity, SK.GetEntity(predef.origin), 0, ai, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, CopyAs::NUMERIC);
for(a = 0; a < 2; a++) {
Entity *e = &(entity->Get(i));
e->CalculateNumerical(false);
CopyEntity(entity, e, a * 2 - (subtype == Subtype::ONE_SIDED ? 0 : 1),
(a == 1) ? REMAP_LATHE_END : REMAP_LATHE_START, h.param(0),
h.param(1), h.param(2), h.param(3), h.param(4), h.param(5),
h.param(6), h.param(7), CopyAs::N_ROT_AXIS_TRANS);
}
// For point entities on the axis, create a construction line
e = &(entity->Get(i));
if(e->IsPoint()) {
Vector check = e->PointGetNum().Minus(axis_pos).Cross(axis_dir);
if (check.Dot(check) < LENGTH_EPS) {
//! @todo isn't this the same as &(ent[i])?
Entity *ep = SK.GetEntity(e->h);
Entity en = {};
// A point gets extruded to form a line segment
en.point[0] = Remap(ep->h, REMAP_LATHE_START);
en.point[1] = Remap(ep->h, REMAP_LATHE_END);
en.group = h;
en.construction = ep->construction;
en.style = ep->style;
en.h = Remap(ep->h, REMAP_PT_TO_LINE);
en.type = Entity::Type::LINE_SEGMENT;
entity->Add(&en);
}
}
ai++;
}
return;
@ -502,16 +677,17 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
}
for(a = a0; a < n; a++) {
// Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) {
Entity *e = &(entity->elem[i]);
if(e->group.v != opA.v) continue;
Entity *e = &(entity->Get(i));
if(e->group != opA) continue;
e->CalculateNumerical(/*forExport=*/false);
CopyEntity(entity, e,
a*2 - (subtype == Subtype::ONE_SIDED ? 0 : (n-1)),
(a == (n - 1)) ? REMAP_LAST : a,
h.param(0), h.param(1), h.param(2),
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
CopyAs::N_TRANS);
}
}
@ -537,16 +713,17 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
}
for(a = a0; a < n; a++) {
// Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < entity->n; i++) {
Entity *e = &(entity->elem[i]);
if(e->group.v != opA.v) continue;
Entity *e = &(entity->Get(i));
if(e->group != opA) continue;
e->CalculateNumerical(/*forExport=*/false);
CopyEntity(entity, e,
a*2 - (subtype == Subtype::ONE_SIDED ? 0 : (n-1)),
(a == (n - 1)) ? REMAP_LAST : a,
h.param(0), h.param(1), h.param(2),
h.param(3), h.param(4), h.param(5), h.param(6),
h.param(3), h.param(4), h.param(5), h.param(6), NO_PARAM,
CopyAs::N_ROT_AA);
}
}
@ -563,11 +740,12 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
AddParam(param, h.param(5), 0);
AddParam(param, h.param(6), 0);
// Not using range-for here because we're changing the size of entity in the loop.
for(i = 0; i < impEntity.n; i++) {
Entity *ie = &(impEntity.elem[i]);
Entity *ie = &(impEntity[i]);
CopyEntity(entity, ie, 0, 0,
h.param(0), h.param(1), h.param(2),
h.param(3), h.param(4), h.param(5), h.param(6),
h.param(3), h.param(4), h.param(5), h.param(6), NO_PARAM,
CopyAs::N_ROT_TRANS);
}
return;
@ -596,7 +774,7 @@ void Group::GenerateEquations(IdList<Equation,hEquation> *l) {
Expr::From(h.param(5)),
Expr::From(h.param(6)) };
AddEq(l, (q.Magnitude())->Minus(Expr::From(1)), 0);
} else if(type == Type::ROTATE) {
} else if(type == Type::ROTATE || type == Type::REVOLVE || type == Type::HELIX) {
// The axis and center of rotation are specified numerically
#define EC(x) (Expr::From(x))
#define EP(x) (Expr::From(h.param(x)))
@ -613,7 +791,7 @@ void Group::GenerateEquations(IdList<Equation,hEquation> *l) {
#undef EC
#undef EP
} else if(type == Type::EXTRUDE) {
if(predef.entityB.v != Entity::FREE_IN_3D.v) {
if(predef.entityB != Entity::FREE_IN_3D) {
// The extrusion path is locked along a line, normal to the
// specified workplane.
Entity *w = SK.GetEntity(predef.entityB);
@ -628,7 +806,7 @@ void Group::GenerateEquations(IdList<Equation,hEquation> *l) {
AddEq(l, v.Dot(extruden), 1);
}
} else if(type == Type::TRANSLATE) {
if(predef.entityB.v != Entity::FREE_IN_3D.v) {
if(predef.entityB != Entity::FREE_IN_3D) {
Entity *w = SK.GetEntity(predef.entityB);
ExprVector n = w->Normal()->NormalExprsN();
ExprVector trans;
@ -644,7 +822,7 @@ hEntity Group::Remap(hEntity in, int copyNumber) {
auto it = remap.find({ in, copyNumber });
if(it == remap.end()) {
std::tie(it, std::ignore) =
remap.insert({ { in, copyNumber }, { (uint32_t)remap.size() } });
remap.insert({ { in, copyNumber }, { (uint32_t)remap.size() + 1 } });
}
return h.entity(it->second.v);
}
@ -729,7 +907,14 @@ void Group::MakeLatheCircles(IdList<Entity,hEntity> *el, IdList<Param,hParam> *p
el->Add(&n);
en.normal = n.h;
el->Add(&en);
} else if(ep->type == Entity::Type::LINE_SEGMENT) {
}
}
void Group::MakeLatheSurfacesSelectable(IdList<Entity, hEntity> *el, hEntity in, Vector axis) {
Entity *ep = SK.GetEntity(in);
Entity en = {};
if(ep->type == Entity::Type::LINE_SEGMENT) {
// An axis-perpendicular line gets revolved to form a face.
Vector a = SK.GetEntity(ep->point[0])->PointGetNum();
Vector b = SK.GetEntity(ep->point[1])->PointGetNum();
@ -786,7 +971,7 @@ void Group::MakeExtrusionTopBottomFaces(IdList<Entity,hEntity> *el, hEntity pt)
void Group::CopyEntity(IdList<Entity,hEntity> *el,
Entity *ep, int timesApplied, int remap,
hParam dx, hParam dy, hParam dz,
hParam qw, hParam qvx, hParam qvy, hParam qvz,
hParam qw, hParam qvx, hParam qvy, hParam qvz, hParam dist,
CopyAs as)
{
Entity en = {};
@ -810,6 +995,7 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
case Entity::Type::POINT_N_TRANS:
case Entity::Type::POINT_N_ROT_TRANS:
case Entity::Type::POINT_N_ROT_AA:
case Entity::Type::POINT_N_ROT_AXIS_TRANS:
case Entity::Type::POINT_IN_3D:
case Entity::Type::POINT_IN_2D:
if(as == CopyAs::N_TRANS) {
@ -822,6 +1008,8 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
} else {
if(as == CopyAs::N_ROT_AA) {
en.type = Entity::Type::POINT_N_ROT_AA;
} else if (as == CopyAs::N_ROT_AXIS_TRANS) {
en.type = Entity::Type::POINT_N_ROT_AXIS_TRANS;
} else {
en.type = Entity::Type::POINT_N_ROT_TRANS;
}
@ -832,6 +1020,9 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
en.param[4] = qvx;
en.param[5] = qvy;
en.param[6] = qvz;
if (as == CopyAs::N_ROT_AXIS_TRANS) {
en.param[7] = dist;
}
}
en.numPoint = (ep->actPoint).ScaledBy(scale);
break;
@ -843,8 +1034,8 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
case Entity::Type::NORMAL_IN_2D:
if(as == CopyAs::N_TRANS || as == CopyAs::NUMERIC) {
en.type = Entity::Type::NORMAL_N_COPY;
} else {
if(as == CopyAs::N_ROT_AA) {
} else { // N_ROT_AXIS_TRANS probably doesn't warrant a new entity Type
if(as == CopyAs::N_ROT_AA || as == CopyAs::N_ROT_AXIS_TRANS) {
en.type = Entity::Type::NORMAL_N_ROT_AA;
} else {
en.type = Entity::Type::NORMAL_N_ROT;
@ -879,7 +1070,7 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
} else if (as == CopyAs::NUMERIC) {
en.type = Entity::Type::FACE_NORMAL_PT;
} else {
if(as == CopyAs::N_ROT_AA) {
if(as == CopyAs::N_ROT_AA || as == CopyAs::N_ROT_AXIS_TRANS) {
en.type = Entity::Type::FACE_N_ROT_AA;
} else {
en.type = Entity::Type::FACE_N_ROT_TRANS;

View File

@ -14,13 +14,15 @@ void Group::AssembleLoops(bool *allClosed,
SBezierList sbl = {};
int i;
for(i = 0; i < SK.entity.n; i++) {
Entity *e = &(SK.entity.elem[i]);
if(e->group.v != h.v) continue;
if(e->construction) continue;
if(e->forceHidden) continue;
for(auto &e : SK.entity) {
if(e.group != h)
continue;
if(e.construction)
continue;
if(e.forceHidden)
continue;
e->GenerateBezierCurves(&sbl);
e.GenerateBezierCurves(&sbl);
}
SBezier *sb;
@ -84,7 +86,7 @@ void SShell::RemapFaces(Group *g, int remap) {
SSurface *ss;
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)){
hEntity face = { ss->face };
if(face.v == Entity::NO_ENTITY.v) continue;
if(face == Entity::NO_ENTITY) continue;
face = g->Remap(face, remap);
ss->face = face.v;
@ -95,7 +97,7 @@ void SMesh::RemapFaces(Group *g, int remap) {
STriangle *tr;
for(tr = l.First(); tr; tr = l.NextAfter(tr)) {
hEntity face = { tr->meta.face };
if(face.v == Entity::NO_ENTITY.v) continue;
if(face == Entity::NO_ENTITY) continue;
face = g->Remap(face, remap);
tr->meta.face = face.v;
@ -191,7 +193,7 @@ void Group::GenerateShellAndMesh() {
// Don't attempt a lathe or extrusion unless the source section is good:
// planar and not self-intersecting.
bool haveSrc = true;
if(type == Type::EXTRUDE || type == Type::LATHE) {
if(type == Type::EXTRUDE || type == Type::LATHE || type == Type::REVOLVE) {
Group *src = SK.GetGroup(opA);
if(src->polyError.how != PolyError::GOOD) {
haveSrc = false;
@ -235,8 +237,10 @@ void Group::GenerateShellAndMesh() {
// that face, so that the user can select them with the mouse.
Vector onOrig = sbls->point;
int i;
// Not using range-for here because we're starting at a different place and using
// indices for meaning.
for(i = is; i < thisShell.surface.n; i++) {
SSurface *ss = &(thisShell.surface.elem[i]);
SSurface *ss = &(thisShell.surface[i]);
hEntity face = Entity::NO_ENTITY;
Vector p = ss->PointAt(0, 0),
@ -261,7 +265,7 @@ void Group::GenerateShellAndMesh() {
Entity *e;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
if(e->group.v != opA.v) continue;
if(e->group != opA) continue;
if(e->type != Entity::Type::LINE_SEGMENT) continue;
Vector a = SK.GetEntity(e->point[0])->PointGetNum(),
@ -293,6 +297,51 @@ void Group::GenerateShellAndMesh() {
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
thisShell.MakeFromRevolutionOf(sbls, pt, axis, color, this);
}
} else if(type == Type::REVOLVE && haveSrc) {
Group *src = SK.GetGroup(opA);
double anglef = SK.GetParam(h.param(3))->val * 4; // why the 4 is needed?
double dists = 0, distf = 0;
double angles = 0.0;
if(subtype != Subtype::ONE_SIDED) {
anglef *= 0.5;
angles = -anglef;
}
Vector pt = SK.GetEntity(predef.origin)->PointGetNum(),
axis = SK.GetEntity(predef.entityB)->VectorGetNum();
axis = axis.WithMagnitude(1);
SBezierLoopSetSet *sblss = &(src->bezierLoops);
SBezierLoopSet *sbls;
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
if(fabs(anglef - angles) < 2 * PI) {
thisShell.MakeFromHelicalRevolutionOf(sbls, pt, axis, color, this,
angles, anglef, dists, distf);
} else {
thisShell.MakeFromRevolutionOf(sbls, pt, axis, color, this);
}
}
} else if(type == Type::HELIX && haveSrc) {
Group *src = SK.GetGroup(opA);
double anglef = SK.GetParam(h.param(3))->val * 4; // why the 4 is needed?
double dists = 0, distf = 0;
double angles = 0.0;
distf = SK.GetParam(h.param(7))->val * 2; // dist is applied twice
if(subtype != Subtype::ONE_SIDED) {
anglef *= 0.5;
angles = -anglef;
distf *= 0.5;
dists = -distf;
}
Vector pt = SK.GetEntity(predef.origin)->PointGetNum(),
axis = SK.GetEntity(predef.entityB)->VectorGetNum();
axis = axis.WithMagnitude(1);
SBezierLoopSetSet *sblss = &(src->bezierLoops);
SBezierLoopSet *sbls;
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
thisShell.MakeFromHelicalRevolutionOf(sbls, pt, axis, color, this,
angles, anglef, dists, distf);
}
} else if(type == Type::LINKED) {
// The imported shell or mesh are copied over, with the appropriate
// transformation applied. We also must remap the face entities.
@ -417,7 +466,7 @@ void Group::GenerateDisplayItems() {
if(SS.GW.showEdges || SS.GW.showOutlines) {
SOutlineList rawOutlines = {};
if(runningMesh.l.n > 0) {
if(!runningMesh.l.IsEmpty()) {
// Triangle mesh only; no shell or emphasized edges.
runningMesh.MakeOutlinesInto(&rawOutlines, EdgeKind::EMPHASIZED);
} else {
@ -437,7 +486,7 @@ void Group::GenerateDisplayItems() {
displayMesh.PrecomputeTransparency();
// Recalculate mass center if needed
if(SS.centerOfMass.draw && SS.centerOfMass.dirty && h.v == SS.GW.activeGroup.v) {
if(SS.centerOfMass.draw && SS.centerOfMass.dirty && h == SS.GW.activeGroup) {
SS.UpdateCenterOfMass();
}
displayDirty = false;
@ -445,13 +494,15 @@ void Group::GenerateDisplayItems() {
}
Group *Group::PreviousGroup() const {
int i;
for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
if(g->h.v == h.v) break;
Group *prev = nullptr;
for(auto const &gh : SK.groupOrder) {
Group *g = SK.GetGroup(gh);
if(g->h == h) {
return prev;
}
prev = g;
}
if(i == 0 || i >= SK.groupOrder.n) return NULL;
return SK.GetGroup(SK.groupOrder.elem[i - 1]);
return nullptr;
}
Group *Group::RunningMeshGroup() const {
@ -466,6 +517,8 @@ bool Group::IsMeshGroup() {
switch(type) {
case Group::Type::EXTRUDE:
case Group::Type::LATHE:
case Group::Type::REVOLVE:
case Group::Type::HELIX:
case Group::Type::ROTATE:
case Group::Type::TRANSLATE:
return true;
@ -653,11 +706,12 @@ void Group::DrawPolyError(Canvas *canvas) {
void Group::DrawFilledPaths(Canvas *canvas) {
for(const SBezierLoopSet &sbls : bezierLoops.l) {
if(sbls.l.n == 0 || sbls.l.elem[0].l.n == 0) continue;
if(sbls.l.IsEmpty() || sbls.l[0].l.IsEmpty())
continue;
// In an assembled loop, all the styles should be the same; so doesn't
// matter which one we grab.
SBezier *sb = &(sbls.l.elem[0].l.elem[0]);
const SBezier *sb = &(sbls.l[0].l[0]);
Style *s = Style::Get({ (uint32_t)sb->auxA });
Canvas::Fill fill = {};
@ -665,7 +719,7 @@ void Group::DrawFilledPaths(Canvas *canvas) {
if(s->filled) {
// This is a filled loop, where the user specified a fill color.
fill.color = s->fillColor;
} else if(h.v == SS.GW.activeGroup.v && SS.checkClosedContour &&
} else if(h == SS.GW.activeGroup && SS.checkClosedContour &&
polyError.how == PolyError::GOOD) {
// If this is the active group, and we are supposed to check
// for closed contours, and we do indeed have a closed and
@ -687,9 +741,10 @@ void Group::DrawContourAreaLabels(Canvas *canvas) {
Vector gu = camera.projUp.ScaledBy(1 / camera.scale);
for(SBezierLoopSet &sbls : bezierLoops.l) {
if(sbls.l.n == 0 || sbls.l.elem[0].l.n == 0) continue;
if(sbls.l.IsEmpty() || sbls.l[0].l.IsEmpty())
continue;
Vector min = sbls.l.elem[0].l.elem[0].ctrl[0];
Vector min = sbls.l[0].l[0].ctrl[0];
Vector max = min;
Vector zero = Vector::From(0.0, 0.0, 0.0);
sbls.GetBoundingProjd(Vector::From(1.0, 0.0, 0.0), zero, &min.x, &max.x);

View File

@ -340,7 +340,7 @@ public:
hStyle styleFor(const DRW_Entity *e) {
// Color.
// TODO: which color to choose: index or RGB one?
//! @todo which color to choose: index or RGB one?
int col = getColor(e);
RgbaColor c = RgbaColor::From(DRW::dxfColors[col][0],
DRW::dxfColors[col][1],
@ -352,7 +352,7 @@ public:
if(width < 0.0) width = 1.0;
// Line stipple.
// TODO: Probably, we can load default autocad patterns and match it with ours.
//! @todo Probably, we can load default autocad patterns and match it with ours.
std::string lineType = getLineType(e);
StipplePattern stipple = StipplePattern::CONTINUOUS;
for(uint32_t i = 0; i <= (uint32_t)StipplePattern::LAST; i++) {
@ -455,8 +455,8 @@ public:
Entity *e = SK.GetEntity(he);
Vector pos = e->PointGetNum();
hEntity p = findPoint(pos);
if(p.v == he.v) return;
if(p.v != Entity::NO_ENTITY.v) {
if(p == he) return;
if(p != Entity::NO_ENTITY) {
if(constrain) {
Constraint::ConstrainCoincident(he, p);
}
@ -475,7 +475,7 @@ public:
hEntity createOrGetPoint(const Vector &p) {
hEntity he = findPoint(p);
if(he.v != Entity::NO_ENTITY.v) return he;
if(he != Entity::NO_ENTITY) return he;
hRequest hr = SS.GW.AddRequest(Request::Type::DATUM_POINT, /*rememberForUndo=*/false);
he = hr.entity(0);

View File

@ -182,7 +182,7 @@ default: dbp("bad constraint type %d", sc->type); return;
c.other2 = (sc->other2) ? true : false;
c.Generate(&params);
if(params.n > 0) {
if(!params.IsEmpty()) {
for(Param &p : params) {
p.h = SK.param.AddAndAssignId(&p);
c.valP = p.h;
@ -240,7 +240,7 @@ default: dbp("bad constraint type %d", sc->type); return;
if(ssys->failed) {
// Copy over any the list of problematic constraints.
for(i = 0; i < ssys->faileds && i < bad.n; i++) {
ssys->failed[i] = bad.elem[i].v;
ssys->failed[i] = bad[i].v;
}
ssys->faileds = bad.n;
}

View File

@ -51,7 +51,7 @@ void SMesh::GetBounding(Vector *vmax, Vector *vmin) const {
*vmin = Vector::From( 1e12, 1e12, 1e12);
*vmax = Vector::From(-1e12, -1e12, -1e12);
for(i = 0; i < l.n; i++) {
STriangle *st = &(l.elem[i]);
const STriangle *st = &(l[i]);
DoBounding(st->a, vmax, vmin);
DoBounding(st->b, vmax, vmin);
DoBounding(st->c, vmax, vmin);
@ -70,7 +70,7 @@ void SMesh::MakeEdgesInPlaneInto(SEdgeList *sel, Vector n, double d) {
m.l.ClearTags();
int i;
for(i = 0; i < m.l.n; i++) {
STriangle *tr = &(m.l.elem[i]);
STriangle *tr = &(m.l[i]);
if((fabs(n.Dot(tr->a) - d) >= LENGTH_EPS) ||
(fabs(n.Dot(tr->b) - d) >= LENGTH_EPS) ||
@ -96,7 +96,7 @@ void SMesh::MakeOutlinesInto(SOutlineList *sol, EdgeKind edgeKind) {
}
//-----------------------------------------------------------------------------
// When we are called, all of the triangles from l.elem[start] to the end must
// When we are called, all of the triangles from l[start] to the end must
// be coplanar. So we try to find a set of fewer triangles that covers the
// exact same area, in order to reduce the number of triangles in the mesh.
// We use this after a triangle has been split against the BSP.
@ -108,7 +108,7 @@ void SMesh::MakeOutlinesInto(SOutlineList *sol, EdgeKind edgeKind) {
void SMesh::Simplify(int start) {
int maxTriangles = (l.n - start) + 10;
STriMeta meta = l.elem[start].meta;
STriMeta meta = l[start].meta;
STriangle *tout = (STriangle *)MemAlloc(maxTriangles*sizeof(*tout));
int toutc = 0;
@ -121,7 +121,7 @@ void SMesh::Simplify(int start) {
int i, j;
for(i = start; i < l.n; i++) {
STriangle *tr = &(l.elem[i]);
STriangle *tr = &(l[i]);
if(tr->MinAltitude() < LENGTH_EPS) {
tr->tag = 1;
} else {
@ -133,7 +133,7 @@ void SMesh::Simplify(int start) {
bool didAdd;
convc = 0;
for(i = start; i < l.n; i++) {
STriangle *tr = &(l.elem[i]);
STriangle *tr = &(l[i]);
if(tr->tag) continue;
tr->tag = 1;
@ -158,7 +158,7 @@ void SMesh::Simplify(int start) {
Vector c;
for(i = start; i < l.n; i++) {
STriangle *tr = &(l.elem[i]);
STriangle *tr = &(l[i]);
if(tr->tag) continue;
if((tr->a).Equals(d) && (tr->b).Equals(b)) {
@ -242,7 +242,7 @@ void SMesh::AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3) {
int i;
for(i = 0; i < srcm->l.n; i++) {
STriangle *st = &(srcm->l.elem[i]);
STriangle *st = &(srcm->l[i]);
int pn = l.n;
atLeastOneDiscarded = false;
SBsp3::InsertOrCreate(bsp3, st, this);
@ -289,7 +289,7 @@ void SMesh::MakeFromDifferenceOf(SMesh *a, SMesh *b) {
void SMesh::MakeFromCopyOf(SMesh *a) {
ssassert(this != a, "Can't make from copy of self");
for(int i = 0; i < a->l.n; i++) {
AddTriangle(&(a->l.elem[i]));
AddTriangle(&(a->l[i]));
}
}
@ -318,9 +318,7 @@ void SMesh::MakeFromTransformationOf(SMesh *a, Vector trans,
}
}
bool SMesh::IsEmpty() const {
return (l.n == 0);
}
bool SMesh::IsEmpty() const { return (l.IsEmpty()); }
uint32_t SMesh::FirstIntersectionWith(Point2d mp) const {
Vector rayPoint = SS.GW.UnProjectPoint3(Vector::From(mp.x, mp.y, 0.0));
@ -329,7 +327,7 @@ uint32_t SMesh::FirstIntersectionWith(Point2d mp) const {
uint32_t face = 0;
double faceT = VERY_NEGATIVE;
for(int i = 0; i < l.n; i++) {
const STriangle &tr = l.elem[i];
const STriangle &tr = l[i];
if(tr.meta.face == 0) continue;
double t;
@ -347,7 +345,7 @@ Vector SMesh::GetCenterOfMass() const {
Vector center = {};
double vol = 0.0;
for(int i = 0; i < l.n; i++) {
STriangle &tr = l.elem[i];
const STriangle &tr = l[i];
double tvol = tr.SignedVolume();
center = center.Plus(tr.a.Plus(tr.b.Plus(tr.c)).ScaledBy(tvol / 4.0));
vol += tvol;
@ -365,7 +363,7 @@ SKdNode *SKdNode::From(SMesh *m) {
STriangle *tra = (STriangle *)AllocTemporary((m->l.n) * sizeof(*tra));
for(i = 0; i < m->l.n; i++) {
tra[i] = m->l.elem[i];
tra[i] = m->l[i];
}
srand(0);
@ -637,7 +635,7 @@ void SKdNode::SnapToVertex(Vector v, SMesh *extras) {
void SKdNode::SnapToMesh(SMesh *m) {
int i, j, k;
for(i = 0; i < m->l.n; i++) {
STriangle *tr = &(m->l.elem[i]);
STriangle *tr = &(m->l[i]);
if(tr->IsDegenerate()) {
continue;
}
@ -649,7 +647,7 @@ void SKdNode::SnapToMesh(SMesh *m) {
for(k = 0; k < extra.l.n; k++) {
STriangle *tra = (STriangle *)AllocTemporary(sizeof(*tra));
*tra = extra.l.elem[k];
*tra = extra.l[k];
AddTriangle(tra);
}
extra.Clear();
@ -1130,3 +1128,67 @@ void SMesh::RemoveDegenerateTriangles() {
}
l.RemoveTagged();
}
double SMesh::CalculateVolume() const {
double vol = 0;
for(STriangle tr : l) {
// Translate to place vertex A at (x, y, 0)
Vector trans = Vector::From(tr.a.x, tr.a.y, 0);
tr.a = (tr.a).Minus(trans);
tr.b = (tr.b).Minus(trans);
tr.c = (tr.c).Minus(trans);
// Rotate to place vertex B on the y-axis. Depending on
// whether the triangle is CW or CCW, C is either to the
// right or to the left of the y-axis. This handles the
// sign of our normal.
Vector u = Vector::From(-tr.b.y, tr.b.x, 0);
u = u.WithMagnitude(1);
Vector v = Vector::From(tr.b.x, tr.b.y, 0);
v = v.WithMagnitude(1);
Vector n = Vector::From(0, 0, 1);
tr.a = (tr.a).DotInToCsys(u, v, n);
tr.b = (tr.b).DotInToCsys(u, v, n);
tr.c = (tr.c).DotInToCsys(u, v, n);
n = tr.Normal().WithMagnitude(1);
// Triangles on edge don't contribute
if(fabs(n.z) < LENGTH_EPS) continue;
// The plane has equation p dot n = a dot n
double d = (tr.a).Dot(n);
// nx*x + ny*y + nz*z = d
// nz*z = d - nx*x - ny*y
double A = -n.x/n.z, B = -n.y/n.z, C = d/n.z;
double mac = tr.c.y/tr.c.x, mbc = (tr.c.y - tr.b.y)/tr.c.x;
double xc = tr.c.x, yb = tr.b.y;
// I asked Maple for
// int(int(A*x + B*y +C, y=mac*x..(mbc*x + yb)), x=0..xc);
double integral =
(1.0/3)*(
A*(mbc-mac)+
(1.0/2)*B*(mbc*mbc-mac*mac)
)*(xc*xc*xc)+
(1.0/2)*(A*yb+B*yb*mbc+C*(mbc-mac))*xc*xc+
C*yb*xc+
(1.0/2)*B*yb*yb*xc;
vol += integral;
}
return vol;
}
double SMesh::CalculateSurfaceArea(const std::vector<uint32_t> &faces) const {
double area = 0.0;
for(uint32_t f : faces) {
for(const STriangle &t : l) {
if(f != t.meta.face) continue;
area += t.Area();
}
}
return area;
}

View File

@ -12,11 +12,11 @@
// Useful when splitting, tangent arcing, or removing bezier points.
//-----------------------------------------------------------------------------
void GraphicsWindow::ReplacePointInConstraints(hEntity oldpt, hEntity newpt) {
int i;
for(i = 0; i < SK.constraint.n; i++) {
Constraint *c = &(SK.constraint.elem[i]);
if(c->ptA.v == oldpt.v) c->ptA = newpt;
if(c->ptB.v == oldpt.v) c->ptB = newpt;
for(auto &c : SK.constraint) {
if(c.ptA == oldpt)
c.ptA = newpt;
if(c.ptB == oldpt)
c.ptB = newpt;
}
}
@ -25,14 +25,13 @@ void GraphicsWindow::ReplacePointInConstraints(hEntity oldpt, hEntity newpt) {
//-----------------------------------------------------------------------------
void GraphicsWindow::RemoveConstraintsForPointBeingDeleted(hEntity hpt) {
SK.constraint.ClearTags();
for(int i = 0; i < SK.constraint.n; i++) {
Constraint *c = &(SK.constraint.elem[i]);
if(c->ptA.v == hpt.v || c->ptB.v == hpt.v) {
c->tag = 1;
for(auto &c : SK.constraint) {
if(c.ptA == hpt || c.ptB == hpt) {
c.tag = 1;
(SS.deleted.constraints)++;
if(c->type != Constraint::Type::POINTS_COINCIDENT &&
c->type != Constraint::Type::HORIZONTAL &&
c->type != Constraint::Type::VERTICAL)
if(c.type != Constraint::Type::POINTS_COINCIDENT &&
c.type != Constraint::Type::HORIZONTAL &&
c.type != Constraint::Type::VERTICAL)
{
(SS.deleted.nonTrivialConstraints)++;
}
@ -49,12 +48,12 @@ void GraphicsWindow::RemoveConstraintsForPointBeingDeleted(hEntity hpt) {
//-----------------------------------------------------------------------------
void GraphicsWindow::FixConstraintsForRequestBeingDeleted(hRequest hr) {
Request *r = SK.GetRequest(hr);
if(r->group.v != SS.GW.activeGroup.v) return;
if(r->group != SS.GW.activeGroup) return;
Entity *e;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
if(!(e->h.isFromRequest())) continue;
if(e->h.request().v != hr.v) continue;
if(e->h.request() != hr) continue;
if(e->type != Entity::Type::POINT_IN_2D &&
e->type != Entity::Type::POINT_IN_3D)
@ -74,13 +73,13 @@ void GraphicsWindow::FixConstraintsForPointBeingDeleted(hEntity hpt) {
SK.constraint.ClearTags();
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
if(c->group.v != SS.GW.activeGroup.v) continue;
if(c->group != SS.GW.activeGroup) continue;
if(c->ptA.v == hpt.v) {
if(c->ptA == hpt) {
ld.Add(&(c->ptB));
c->tag = 1;
}
if(c->ptB.v == hpt.v) {
if(c->ptB == hpt) {
ld.Add(&(c->ptA));
c->tag = 1;
}
@ -97,9 +96,8 @@ void GraphicsWindow::FixConstraintsForPointBeingDeleted(hEntity hpt) {
// those two points were implicitly coincident with each other. By
// deleting hpt (and all constraints that mention it), we will delete
// that relationship. So put it back here now.
int i;
for(i = 1; i < ld.n; i++) {
Constraint::ConstrainCoincident(ld.elem[i-1], ld.elem[i]);
for(int i = 1; i < ld.n; i++) {
Constraint::ConstrainCoincident(ld[i-1], ld[i]);
}
ld.Clear();
}
@ -233,10 +231,10 @@ void GraphicsWindow::ParametricCurve::ConstrainPointIfCoincident(hEntity hpt) {
ptv = pt->PointGetNum();
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
if(e->h.v == pt->h.v) continue;
if(e->h == pt->h) continue;
if(!e->IsPoint()) continue;
if(e->group.v != pt->group.v) continue;
if(e->workplane.v != pt->workplane.v) continue;
if(e->group != pt->group) continue;
if(e->workplane != pt->workplane) continue;
ev = e->PointGetNum();
if(!ev.Equals(ptv)) continue;
@ -270,18 +268,18 @@ void GraphicsWindow::MakeTangentArc() {
hRequest hreq[2];
hEntity hent[2];
bool pointf[2];
for(i = 0; i < SK.request.n; i++) {
Request *r = &(SK.request.elem[i]);
if(r->group.v != activeGroup.v) continue;
if(r->workplane.v != ActiveWorkplane().v) continue;
if(r->construction) continue;
if(r->type != Request::Type::LINE_SEGMENT &&
r->type != Request::Type::ARC_OF_CIRCLE)
{
for(auto &r : SK.request) {
if(r.group != activeGroup)
continue;
if(r.workplane != ActiveWorkplane())
continue;
if(r.construction)
continue;
if(r.type != Request::Type::LINE_SEGMENT && r.type != Request::Type::ARC_OF_CIRCLE) {
continue;
}
Entity *e = SK.GetEntity(r->h.entity(0));
Entity *e = SK.GetEntity(r.h.entity(0));
Vector ps = e->EndpointStart(),
pf = e->EndpointFinish();
@ -292,8 +290,8 @@ void GraphicsWindow::MakeTangentArc() {
// finish of this entity.
ent[c] = e;
hent[c] = e->h;
req[c] = r;
hreq[c] = r->h;
req[c] = &r;
hreq[c] = r.h;
pointf[c] = (pf.Equals(pshared));
}
c++;
@ -375,8 +373,8 @@ void GraphicsWindow::MakeTangentArc() {
tp[1] = t[1];
// And convert those points to parameter values along the curve.
t[0] += (pa0.Minus(p0)).DivPivoting(t0);
t[1] += (pa1.Minus(p1)).DivPivoting(t1);
t[0] += (pa0.Minus(p0)).DivProjected(t0);
t[1] += (pa1.Minus(p1)).DivProjected(t1);
}
// Stupid check for convergence, and for an out of range result (as
@ -411,9 +409,9 @@ void GraphicsWindow::MakeTangentArc() {
// Delete the coincident constraint for the removed point.
SK.constraint.ClearTags();
for(i = 0; i < SK.constraint.n; i++) {
Constraint *cs = &(SK.constraint.elem[i]);
if(cs->group.v != activeGroup.v) continue;
if(cs->workplane.v != ActiveWorkplane().v) continue;
Constraint *cs = &(SK.constraint[i]);
if(cs->group != activeGroup) continue;
if(cs->workplane != ActiveWorkplane()) continue;
if(cs->type != Constraint::Type::POINTS_COINCIDENT) continue;
if (SK.GetEntity(cs->ptA)->PointGetNum().Equals(pshared)) {
cs->tag = 1;
@ -536,7 +534,7 @@ hEntity GraphicsWindow::SplitCubic(hEntity he, Vector pinter) {
double t;
int i, j;
for(i = 0; i < sbl.l.n; i++) {
SBezier *sb = &(sbl.l.elem[i]);
SBezier *sb = &(sbl.l[i]);
ssassert(sb->deg == 3, "Expected a cubic bezier");
sb->ClosestPointTo(pinter, &t, /*mustConverge=*/false);
@ -603,15 +601,16 @@ hEntity GraphicsWindow::SplitEntity(hEntity he, Vector pinter) {
// Finally, delete the request that generated the original entity.
Request::Type reqType = EntReqTable::GetRequestForEntity(entityType);
SK.request.ClearTags();
for(int i = 0; i < SK.request.n; i++) {
Request *r = &(SK.request.elem[i]);
if(r->group.v != activeGroup.v) continue;
if(r->type != reqType) continue;
for(auto &r : SK.request) {
if(r.group != activeGroup)
continue;
if(r.type != reqType)
continue;
// If the user wants to keep the old entities around, they can just
// mark them construction first.
if(he.v == r->h.entity(0).v && !r->construction) {
r->tag = 1;
if(he == r.h.entity(0) && !r.construction) {
r.tag = 1;
break;
}
}
@ -659,8 +658,8 @@ void GraphicsWindow::SplitLinesOrCurves() {
}
for(Constraint &c : SK.constraint) {
if(c.ptA.request().v == hb.request().v &&
c.entityA.request().v == ha.request().v) {
if(c.ptA.request() == hb.request() &&
c.entityA.request() == ha.request()) {
pi = SK.GetEntity(c.ptA)->PointGetNum();
if(ea->type == Entity::Type::LINE_SEGMENT && !pi.OnLineSegment(p0, p1)) {
@ -682,7 +681,7 @@ void GraphicsWindow::SplitLinesOrCurves() {
sbla.AllIntersectionsWith(&sblb, &inters);
// If there's multiple points, then take the one closest to the mouse pointer.
if(inters.l.n > 0) {
if(!inters.l.IsEmpty()) {
double dmin = VERY_POSITIVE;
SPoint *sp;
for(sp = inters.l.First(); sp; sp = inters.l.NextAfter(sp)) {
@ -713,7 +712,7 @@ void GraphicsWindow::SplitLinesOrCurves() {
// Remove datum point, as it has now been superseded by the split point.
SK.request.ClearTags();
for(Request &r : SK.request) {
if(r.h.v == hb.request().v) {
if(r.h == hb.request()) {
if(r.type == Request::Type::DATUM_POINT) {
// Delete datum point.
r.tag = 1;

View File

@ -24,7 +24,7 @@ void GraphicsWindow::AddPointToDraggedList(hEntity hp) {
// twice as far as the mouse pointer...
List<hEntity> *lhe = &(pending.points);
for(hEntity *hee = lhe->First(); hee; hee = lhe->NextAfter(hee)) {
if(hee->v == hp.v) {
if(*hee == hp) {
// Exact same point.
return;
}
@ -32,7 +32,7 @@ void GraphicsWindow::AddPointToDraggedList(hEntity hp) {
if(pe->type == p->type &&
pe->type != Entity::Type::POINT_IN_2D &&
pe->type != Entity::Type::POINT_IN_3D &&
pe->group.v == p->group.v)
pe->group == p->group)
{
// Transform-type point, from the same group. So it handles the
// same unknowns.
@ -75,7 +75,7 @@ void GraphicsWindow::StartDraggingBySelection() {
// the hovered item too, and they'll always have it.
if(hover.entity.v) {
hEntity dragEntity = ChooseFromHoverToDrag().entity;
if(dragEntity.v != Entity::NO_ENTITY.v) {
if(dragEntity != Entity::NO_ENTITY) {
StartDraggingByEntity(dragEntity);
}
}
@ -383,7 +383,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
HitTestMakeSelection(mp);
hRequest hr = pending.point.request();
if(pending.point.v == hr.entity(4).v) {
if(pending.point == hr.entity(4)) {
// The very first segment; dragging final point drags both
// tangent points.
Vector p0 = SK.GetEntity(hr.entity(1))->PointGetNum(),
@ -486,7 +486,7 @@ void GraphicsWindow::ClearPending(bool scheduleShowTW) {
bool GraphicsWindow::IsFromPending(hRequest r) {
for(auto &req : pending.requests) {
if(req.v == r.v) return true;
if(req == r) return true;
}
return false;
}
@ -497,8 +497,8 @@ void GraphicsWindow::AddToPending(hRequest r) {
void GraphicsWindow::ReplacePending(hRequest before, hRequest after) {
for(auto &req : pending.requests) {
if(req.v == before.v) {
req.v = after.v;
if(req == before) {
req = after;
}
}
}
@ -721,7 +721,7 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
IdList<Constraint,hConstraint> *lc = &(SK.constraint);
for(c = lc->First(); c; c = lc->NextAfter(c)) {
if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
if(c->ptA.v == p->h.v || c->ptB.v == p->h.v) {
if(c->ptA == p->h || c->ptB == p->h) {
break;
}
}
@ -734,7 +734,7 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
Constraint *c;
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
if(c->ptA.v == p->h.v || c->ptB.v == p->h.v) {
if(c->ptA == p->h || c->ptB == p->h) {
c->tag = 1;
}
}
@ -755,7 +755,7 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
[&]() { MenuEdit(Command::SELECT_ALL); });
}
if((SS.clipboard.r.n > 0 || SS.clipboard.c.n > 0) && LockedInWorkplane()) {
if((!SS.clipboard.r.IsEmpty() || !SS.clipboard.c.IsEmpty()) && LockedInWorkplane()) {
menu->AddItem(_("Paste"),
[&]() { MenuClipboard(Command::PASTE); });
menu->AddItem(_("Paste Transformed..."),
@ -1178,7 +1178,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my, bool shiftDown, bool ct
hRequest hr = pending.point.request();
Request *r = SK.GetRequest(hr);
if(hover.entity.v == hr.entity(1).v && r->extraPoints >= 2) {
if(hover.entity == hr.entity(1) && r->extraPoints >= 2) {
// They want the endpoints coincident, which means a periodic
// spline instead.
r->type = Request::Type::CUBIC_PERIODIC;
@ -1543,7 +1543,7 @@ void GraphicsWindow::SixDofEvent(Platform::SixDofEvent event) {
// point.
int64_t now = GetMilliseconds();
if(now - last6DofTime > 5000 ||
last6DofGroup.v != g->h.v)
last6DofGroup != g->h)
{
SS.UndoRemember();
}

View File

@ -312,7 +312,7 @@ static bool RunCommand(const std::vector<std::string> args) {
return false;
}
if(inputFiles.size() == 0) {
if(inputFiles.empty()) {
fprintf(stderr, "At least one input file must be specified.\n");
return false;
}

View File

@ -29,8 +29,8 @@ int main(int argc, char** argv) {
Platform::Close3DConnexion();
SS.Clear();
SK.Clear();
Platform::ClearGui();
return 0;
}

View File

@ -95,6 +95,7 @@ std::vector<FileFilter> MeshFileFilters = {
{ CN_("file-type", "Three.js-compatible mesh, with viewer"), { "html" } },
{ CN_("file-type", "Three.js-compatible mesh, mesh only"), { "js" } },
{ CN_("file-type", "Q3D Object file"), { "q3do" } },
{ CN_("file-type", "VRML text file"), { "wrl" } },
};
std::vector<FileFilter> SurfaceFileFilters = {

View File

@ -107,7 +107,7 @@ void FatalError(std::string message);
// A native settings store.
class Settings {
public:
virtual ~Settings() {}
virtual ~Settings() = default;
virtual void FreezeInt(const std::string &key, uint32_t value) = 0;
virtual uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) = 0;
@ -135,7 +135,7 @@ class Timer {
public:
std::function<void()> onTimeout;
virtual ~Timer() {}
virtual ~Timer() = default;
virtual void RunAfter(unsigned milliseconds) = 0;
virtual void RunAfterNextFrame() { RunAfter(1); }
@ -157,7 +157,7 @@ public:
std::function<void()> onTrigger;
virtual ~MenuItem() {}
virtual ~MenuItem() = default;
virtual void SetAccelerator(KeyboardEvent accel) = 0;
virtual void SetIndicator(Indicator type) = 0;
@ -170,7 +170,7 @@ typedef std::shared_ptr<MenuItem> MenuItemRef;
// A native menu.
class Menu {
public:
virtual ~Menu() {}
virtual ~Menu() = default;
virtual std::shared_ptr<MenuItem> AddItem(
const std::string &label, std::function<void()> onTrigger = std::function<void()>(),
@ -188,7 +188,7 @@ typedef std::shared_ptr<Menu> MenuRef;
// A native menu bar.
class MenuBar {
public:
virtual ~MenuBar() {}
virtual ~MenuBar() = default;
virtual std::shared_ptr<Menu> AddSubMenu(const std::string &label) = 0;
@ -222,7 +222,7 @@ public:
std::function<void(double)> onScrollbarAdjusted;
std::function<void()> onRender;
virtual ~Window() {}
virtual ~Window() = default;
// Returns physical display DPI.
virtual double GetPixelDensity() = 0;
@ -299,7 +299,7 @@ public:
std::function<void(Response)> onResponse;
virtual ~MessageDialog() {}
virtual ~MessageDialog() = default;
virtual void SetType(Type type) = 0;
virtual void SetTitle(std::string title) = 0;
@ -347,7 +347,7 @@ extern std::vector<FileFilter> CsvFileFilters;
// A native dialog that asks to choose a file.
class FileDialog {
public:
virtual ~FileDialog() {}
virtual ~FileDialog() = default;
virtual void SetTitle(std::string title) = 0;
virtual void SetCurrentName(std::string name) = 0;
@ -380,6 +380,7 @@ void OpenInBrowser(const std::string &url);
void InitGui(int argc, char **argv);
void RunGui();
void ExitGui();
void ClearGui();
}

View File

@ -124,7 +124,7 @@ public:
}
}
~SettingsImplGtk() {
~SettingsImplGtk() override {
if(!_path.IsEmpty()) {
// json-c <0.12 has the first argument non-const
if(json_object_to_file_ext((char *)_path.raw.c_str(), _json,
@ -1292,7 +1292,8 @@ public:
void FilterChanged() {
std::string extension = GetExtension();
if(extension == "") return;
if(extension.empty())
return;
Platform::Path path = GetFilename();
SetCurrentName(path.WithExtension(extension).FileName());
@ -1470,11 +1471,14 @@ void InitGui(int argc, char **argv) {
}
void RunGui() {
gtkMain->run();
Gtk::Main::run();
}
void ExitGui() {
gtkMain->quit();
Gtk::Main::quit();
}
void ClearGui() {
delete gtkMain;
}

View File

@ -1479,5 +1479,7 @@ void ExitGui() {
[NSApp terminate:nil];
}
void ClearGui() {}
}
}

View File

@ -53,22 +53,24 @@ void FatalError(std::string message) {
class SettingsImplDummy final : public Settings {
public:
void FreezeInt(const std::string &key, uint32_t value) {}
void FreezeInt(const std::string &key, uint32_t value) override {
}
uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) {
uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) override {
return defaultValue;
}
void FreezeFloat(const std::string &key, double value) {}
void FreezeFloat(const std::string &key, double value) override {
}
double ThawFloat(const std::string &key, double defaultValue = 0.0) {
double ThawFloat(const std::string &key, double defaultValue = 0.0) override {
return defaultValue;
}
void FreezeString(const std::string &key, const std::string &value) {}
void FreezeString(const std::string &key, const std::string &value) override {
}
std::string ThawString(const std::string &key,
const std::string &defaultValue = "") {
std::string ThawString(const std::string &key, const std::string &defaultValue = "") override {
return defaultValue;
}
};
@ -154,6 +156,8 @@ void ExitGui() {
exit(0);
}
void ClearGui() {}
}
}

View File

@ -1369,7 +1369,9 @@ public:
}
};
#if HAVE_OPENGL == 3
EGLDisplay WindowImplWin32::eglDisplay = EGL_NO_DISPLAY;
#endif
WindowRef CreateWindow(Window::Kind kind, WindowRef parentWindow) {
return std::make_shared<WindowImplWin32>(kind,
@ -1675,5 +1677,7 @@ void ExitGui() {
PostQuitMessage(0);
}
void ClearGui() {}
}
}

View File

@ -203,7 +203,7 @@ static void FindPrefix(const std::string &raw, size_t *pos) {
}
}
#else
if(raw.size() >= 1 && raw[0] == '/') {
if(!raw.empty() && raw[0] == '/') {
*pos = 1;
}
#endif
@ -295,7 +295,7 @@ Path Path::Expand(bool fromCurrentDirectory) const {
if(expanded.IsEmpty()) {
if(expandedComponents.empty()) {
expandedComponents.push_back(".");
expandedComponents.emplace_back(".");
}
expanded = From(Concat(expandedComponents, SEPARATOR));
} else if(!expandedComponents.empty()) {
@ -371,12 +371,12 @@ Path Path::RelativeTo(const Path &base) const {
std::vector<std::string> resultComponents;
for(size_t i = common; i < baseComponents.size(); i++) {
resultComponents.push_back("..");
resultComponents.emplace_back("..");
}
resultComponents.insert(resultComponents.end(),
components.begin() + common, components.end());
if(resultComponents.empty()) {
resultComponents.push_back(".");
resultComponents.emplace_back(".");
}
return From(Concat(resultComponents, SEPARATOR));
}

View File

@ -54,8 +54,7 @@ void *AllocTemporary(size_t n)
return (void *)&h[1];
}
void FreeAllTemporary(void)
{
void FreeAllTemporary() {
AllocTempHeader *h = Head;
while(h) {
AllocTempHeader *f = h;
@ -77,8 +76,9 @@ void MemFree(void *p) {
std::vector<std::string> InitPlatform(int argc, char **argv) {
std::vector<std::string> args;
args.reserve(argc);
for(int i = 0; i < argc; i++) {
args.push_back(argv[i]);
args.emplace_back(argv[i]);
}
return args;
}

View File

@ -87,6 +87,12 @@ double STriangle::SignedVolume() const {
return a.Dot(b.Cross(c)) / 6.0;
}
double STriangle::Area() const {
Vector ab = a.Minus(b);
Vector cb = c.Minus(b);
return ab.Cross(cb).Magnitude() / 2.0;
}
bool STriangle::IsDegenerate() const {
return a.OnLineSegment(b, c) ||
b.OnLineSegment(a, c) ||
@ -158,13 +164,13 @@ bool SEdge::EdgeCrosses(Vector ea, Vector eb, Vector *ppi, SPointList *spl) cons
// on the other
bool inters = false;
double t;
t = a.Minus(ea).DivPivoting(d);
t = a.Minus(ea).DivProjected(d);
if(t > t_eps && t < (1 - t_eps)) inters = true;
t = b.Minus(ea).DivPivoting(d);
t = b.Minus(ea).DivProjected(d);
if(t > t_eps && t < (1 - t_eps)) inters = true;
t = ea.Minus(a).DivPivoting(dthis);
t = ea.Minus(a).DivProjected(dthis);
if(t > tthis_eps && t < (1 - tthis_eps)) inters = true;
t = eb.Minus(a).DivPivoting(dthis);
t = eb.Minus(a).DivProjected(dthis);
if(t > tthis_eps && t < (1 - tthis_eps)) inters = true;
if(inters) {
@ -227,7 +233,8 @@ bool SEdgeList::AssembleContour(Vector first, Vector last, SContour *dest,
do {
for(i = 0; i < l.n; i++) {
SEdge *se = &(l.elem[i]);
/// @todo fix const!
SEdge *se = const_cast<SEdge*>(&(l[i]));
if(se->tag) continue;
if(se->a.Equals(last)) {
@ -267,10 +274,11 @@ bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt, bool keepDir) co
Vector last = Vector::From(0, 0, 0);
int i;
for(i = 0; i < l.n; i++) {
if(!l.elem[i].tag) {
first = l.elem[i].a;
last = l.elem[i].b;
l.elem[i].tag = 1;
if(!l[i].tag) {
first = l[i].a;
last = l[i].b;
/// @todo fix const!
const_cast<SEdge*>(&(l[i]))->tag = 1;
break;
}
}
@ -281,9 +289,7 @@ bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt, bool keepDir) co
// Create a new empty contour in our polygon, and finish assembling
// into that contour.
dest->AddEmptyContour();
if(!AssembleContour(first, last, &(dest->l.elem[dest->l.n-1]),
errorAt, keepDir))
{
if(!AssembleContour(first, last, dest->l.Last(), errorAt, keepDir)) {
allClosed = false;
}
// But continue assembling, even if some of the contours are open
@ -335,11 +341,10 @@ bool SEdgeList::ContainsEdge(const SEdge *set) const {
//-----------------------------------------------------------------------------
void SEdgeList::CullExtraneousEdges(bool both) {
l.ClearTags();
int i, j;
for(i = 0; i < l.n; i++) {
SEdge *se = &(l.elem[i]);
for(j = i+1; j < l.n; j++) {
SEdge *set = &(l.elem[j]);
for(int i = 0; i < l.n; i++) {
SEdge *se = &(l[i]);
for(int j = i + 1; j < l.n; j++) {
SEdge *set = &(l[j]);
if((set->a).Equals(se->a) && (set->b).Equals(se->b)) {
// Two parallel edges exist; so keep only the first one.
set->tag = 1;
@ -491,33 +496,27 @@ int SKdNodeEdges::AnyEdgeCrossings(Vector a, Vector b, int cnt,
// We have an edge list that contains only collinear edges, maybe with more
// splits than necessary. Merge any collinear segments that join.
//-----------------------------------------------------------------------------
static Vector LineStart, LineDirection;
static int ByTAlongLine(const void *av, const void *bv)
{
SEdge *a = (SEdge *)av,
*b = (SEdge *)bv;
double ta = (a->a.Minus(LineStart)).DivPivoting(LineDirection),
tb = (b->a.Minus(LineStart)).DivPivoting(LineDirection);
return (ta > tb) ? 1 : -1;
}
void SEdgeList::MergeCollinearSegments(Vector a, Vector b) {
LineStart = a;
LineDirection = b.Minus(a);
qsort(l.elem, l.n, sizeof(l.elem[0]), ByTAlongLine);
const Vector lineStart = a;
const Vector lineDirection = b.Minus(a);
std::sort(l.begin(), l.end(), [&](const SEdge &a, const SEdge &b) {
double ta = (a.a.Minus(lineStart)).DivProjected(lineDirection);
double tb = (b.a.Minus(lineStart)).DivProjected(lineDirection);
return (ta < tb);
});
l.ClearTags();
int i;
for(i = 1; i < l.n; i++) {
SEdge *prev = &(l.elem[i-1]),
*now = &(l.elem[i]);
if((prev->b).Equals(now->a) && prev->auxA == now->auxA) {
// The previous segment joins up to us; so merge it into us.
prev->tag = 1;
now->a = prev->a;
SEdge *prev = nullptr;
for(auto &now : l) {
if(prev != nullptr) {
if((prev->b).Equals(now.a) && prev->auxA == now.auxA) {
// The previous segment joins up to us; so merge it into us.
prev->tag = 1;
now.a = prev->a;
}
}
prev = &now;
}
l.RemoveTagged();
}
@ -533,7 +532,7 @@ bool SPointList::ContainsPoint(Vector pt) const {
int SPointList::IndexForPoint(Vector pt) const {
int i;
for(i = 0; i < l.n; i++) {
SPoint *p = &(l.elem[i]);
const SPoint *p = &(l[i]);
if(pt.Equals(p->p)) {
return i;
}
@ -573,7 +572,7 @@ void SContour::AddPoint(Vector p) {
void SContour::MakeEdgesInto(SEdgeList *el) const {
int i;
for(i = 0; i < (l.n - 1); i++) {
el->AddEdge(l.elem[i].p, l.elem[i+1].p);
el->AddEdge(l[i].p, l[i+1].p);
}
}
@ -596,8 +595,8 @@ Vector SContour::ComputeNormal() const {
Vector n = Vector::From(0, 0, 0);
for(int i = 0; i < l.n - 2; i++) {
Vector u = (l.elem[i+1].p).Minus(l.elem[i+0].p).WithMagnitude(1);
Vector v = (l.elem[i+2].p).Minus(l.elem[i+1].p).WithMagnitude(1);
Vector u = (l[i+1].p).Minus(l[i+0].p).WithMagnitude(1);
Vector v = (l[i+2].p).Minus(l[i+1].p).WithMagnitude(1);
Vector nt = u.Cross(v);
if(nt.Magnitude() > n.Magnitude()) {
n = nt;
@ -608,7 +607,7 @@ Vector SContour::ComputeNormal() const {
Vector SContour::AnyEdgeMidpoint() const {
ssassert(l.n >= 2, "Need two points to find a midpoint");
return ((l.elem[0].p).Plus(l.elem[1].p)).ScaledBy(0.5);
return ((l[0].p).Plus(l[1].p)).ScaledBy(0.5);
}
bool SContour::IsClockwiseProjdToNormal(Vector n) const {
@ -626,10 +625,10 @@ double SContour::SignedAreaProjdToNormal(Vector n) const {
double area = 0;
for(int i = 0; i < (l.n - 1); i++) {
double u0 = (l.elem[i ].p).Dot(u);
double v0 = (l.elem[i ].p).Dot(v);
double u1 = (l.elem[i+1].p).Dot(u);
double v1 = (l.elem[i+1].p).Dot(v);
double u0 = (l[i ].p).Dot(u);
double v0 = (l[i ].p).Dot(v);
double u1 = (l[i+1].p).Dot(u);
double v1 = (l[i+1].p).Dot(v);
area += ((v0 + v1)/2)*(u1 - u0);
}
@ -645,11 +644,11 @@ bool SContour::ContainsPointProjdToNormal(Vector n, Vector p) const {
bool inside = false;
for(int i = 0; i < (l.n - 1); i++) {
double ua = (l.elem[i ].p).Dot(u);
double va = (l.elem[i ].p).Dot(v);
double ua = (l[i ].p).Dot(u);
double va = (l[i ].p).Dot(v);
// The curve needs to be exactly closed; approximation is death.
double ub = (l.elem[(i+1)%(l.n-1)].p).Dot(u);
double vb = (l.elem[(i+1)%(l.n-1)].p).Dot(v);
double ub = (l[(i+1)%(l.n-1)].p).Dot(u);
double vb = (l[(i+1)%(l.n-1)].p).Dot(v);
if ((((va <= vp) && (vp < vb)) ||
((vb <= vp) && (vp < va))) &&
@ -670,7 +669,7 @@ void SContour::Reverse() {
void SPolygon::Clear() {
int i;
for(i = 0; i < l.n; i++) {
(l.elem[i]).l.Clear();
(l[i]).l.Clear();
}
l.Clear();
}
@ -683,13 +682,14 @@ void SPolygon::AddEmptyContour() {
void SPolygon::MakeEdgesInto(SEdgeList *el) const {
int i;
for(i = 0; i < l.n; i++) {
(l.elem[i]).MakeEdgesInto(el);
(l[i]).MakeEdgesInto(el);
}
}
Vector SPolygon::ComputeNormal() const {
if(l.n < 1) return Vector::From(0, 0, 0);
return (l.elem[0]).ComputeNormal();
if(l.IsEmpty())
return Vector::From(0, 0, 0);
return (l[0]).ComputeNormal();
}
double SPolygon::SignedArea() const {
@ -710,7 +710,7 @@ int SPolygon::WindingNumberForPoint(Vector p) const {
int winding = 0;
int i;
for(i = 0; i < l.n; i++) {
SContour *sc = &(l.elem[i]);
const SContour *sc = &(l[i]);
if(sc->ContainsPointProjdToNormal(normal, p)) {
winding++;
}
@ -725,18 +725,18 @@ void SPolygon::FixContourDirections() {
// Outside curve looks counterclockwise, projected against our normal.
int i, j;
for(i = 0; i < l.n; i++) {
SContour *sc = &(l.elem[i]);
SContour *sc = &(l[i]);
if(sc->l.n < 2) continue;
// The contours may not intersect, but they may share vertices; so
// testing a vertex for point-in-polygon may fail, but the midpoint
// of an edge is okay.
Vector pt = (((sc->l.elem[0]).p).Plus(sc->l.elem[1].p)).ScaledBy(0.5);
Vector pt = (((sc->l[0]).p).Plus(sc->l[1].p)).ScaledBy(0.5);
sc->timesEnclosed = 0;
bool outer = true;
for(j = 0; j < l.n; j++) {
if(i == j) continue;
SContour *sct = &(l.elem[j]);
SContour *sct = &(l[j]);
if(sct->ContainsPointProjdToNormal(normal, pt)) {
outer = !outer;
(sc->timesEnclosed)++;
@ -752,13 +752,14 @@ void SPolygon::FixContourDirections() {
}
bool SPolygon::IsEmpty() const {
if(l.n == 0 || l.elem[0].l.n == 0) return true;
if(l.IsEmpty() || l[0].l.IsEmpty())
return true;
return false;
}
Vector SPolygon::AnyPoint() const {
ssassert(!IsEmpty(), "Need at least one point");
return l.elem[0].l.elem[0].p;
return l[0].l[0].p;
}
bool SPolygon::SelfIntersecting(Vector *intersectsAt) const {
@ -804,9 +805,9 @@ void SPolygon::OffsetInto(SPolygon *dest, double r) const {
int i;
dest->Clear();
for(i = 0; i < l.n; i++) {
SContour *sc = &(l.elem[i]);
const SContour *sc = &(l[i]);
dest->AddEmptyContour();
sc->OffsetInto(&(dest->l.elem[dest->l.n-1]), r);
sc->OffsetInto(&(dest->l[dest->l.n-1]), r);
}
}
//-----------------------------------------------------------------------------
@ -861,9 +862,9 @@ void SContour::OffsetInto(SContour *dest, double r) const {
Vector dp, dn;
double thetan, thetap;
a = l.elem[WRAP(i-1, (l.n-1))].p;
b = l.elem[WRAP(i, (l.n-1))].p;
c = l.elem[WRAP(i+1, (l.n-1))].p;
a = l[WRAP(i-1, (l.n-1))].p;
b = l[WRAP(i, (l.n-1))].p;
c = l[WRAP(i+1, (l.n-1))].p;
dp = a.Minus(b);
thetap = atan2(dp.y, dp.x);

View File

@ -191,6 +191,7 @@ public:
bool Raytrace(const Vector &rayPoint, const Vector &rayDir,
double *t, Vector *inters) const;
double SignedVolume() const;
double Area() const;
bool IsDegenerate() const;
};
@ -280,6 +281,8 @@ public:
void PrecomputeTransparency();
void RemoveDegenerateTriangles();
double CalculateVolume() const;
double CalculateSurfaceArea(const std::vector<uint32_t> &faces) const;
bool IsEmpty() const;
void RemapFaces(Group *g, int remap);

View File

@ -244,7 +244,7 @@ MeshRenderer::Handle MeshRenderer::Add(const SMesh &m, bool dynamic) {
MeshVertex *vertices = new MeshVertex[m.l.n * 3];
for(int i = 0; i < m.l.n; i++) {
const STriangle &t = m.l.elem[i];
const STriangle &t = m.l[i];
vertices[i * 3 + 0].pos = Vector3f::From(t.a);
vertices[i * 3 + 1].pos = Vector3f::From(t.b);
vertices[i * 3 + 2].pos = Vector3f::From(t.c);
@ -395,7 +395,7 @@ GLuint Generate(const std::vector<double> &pattern) {
int dashI = 0;
double dashT = 0.0;
for(int i = 0; i < size; i++) {
if(pattern.size() == 0) {
if(pattern.empty()) {
textureData[i] = EncodeLengthAsFloat(0.0);
continue;
}
@ -485,8 +485,8 @@ EdgeRenderer::Handle EdgeRenderer::Add(const SEdgeList &edges, bool dynamic) {
uint32_t curVertex = 0;
uint32_t curIndex = 0;
for(int i = 0; i < edges.l.n; i++) {
const SEdge &curr = edges.l.elem[i];
const SEdge &next = edges.l.elem[(i + 1) % edges.l.n];
const SEdge &curr = edges.l[i];
const SEdge &next = edges.l[(i + 1) % edges.l.n];
// 3d positions
Vector3f a = Vector3f::From(curr.a);
@ -674,8 +674,8 @@ OutlineRenderer::Handle OutlineRenderer::Add(const SOutlineList &outlines, bool
uint32_t curIndex = 0;
for(int i = 0; i < outlines.l.n; i++) {
const SOutline &curr = outlines.l.elem[i];
const SOutline &next = outlines.l.elem[(i + 1) % outlines.l.n];
const SOutline &curr = outlines.l[i];
const SOutline &next = outlines.l[(i + 1) % outlines.l.n];
// 3d positions
Vector3f a = Vector3f::From(curr.a);

View File

@ -142,6 +142,7 @@ public:
BitmapFont bitmapFont = {};
virtual void Clear();
virtual ~Canvas() = default;
hStroke GetStroke(const Stroke &stroke);
hFill GetFill(const Fill &fill);
@ -172,6 +173,13 @@ public:
virtual std::shared_ptr<BatchCanvas> CreateBatch();
};
template<>
struct IsHandleOracle<Canvas::hStroke> : std::true_type {};
template<>
struct IsHandleOracle<Canvas::hFill> : std::true_type {};
// An interface for view-dependent visualization.
class ViewportCanvas : public Canvas {
public:
@ -276,7 +284,7 @@ public:
const Camera &GetCamera() const override { return camera; }
// ViewportCanvas interface.
void SetCamera(const Camera &camera) override { this->camera = camera; };
void SetCamera(const Camera &camera) override { this->camera = camera; }
void SetLighting(const Lighting &lighting) override { this->lighting = lighting; }
void DrawLine(const Vector &a, const Vector &b, hStroke hcs) override;
@ -353,7 +361,7 @@ public:
void OutputEnd() override;
};
class CairoPixmapRenderer : public CairoRenderer {
class CairoPixmapRenderer final : public CairoRenderer {
public:
std::shared_ptr<Pixmap> pixmap;

View File

@ -243,7 +243,7 @@ void SurfaceRenderer::ConvertBeziersToEdges() {
List<Vector> lv = {};
b.MakePwlInto(&lv, chordTolerance);
for(int i = 1; i < lv.n; i++) {
el.AddEdge(lv.elem[i-1], lv.elem[i]);
el.AddEdge(lv[i-1], lv[i]);
}
lv.Clear();
}
@ -255,7 +255,8 @@ void SurfaceRenderer::ConvertBeziersToEdges() {
void SurfaceRenderer::CullOccludedStrokes() {
// Perform occlusion testing, if necessary.
if(mesh.l.n == 0) return;
if(mesh.l.IsEmpty())
return;
// We can't perform hidden line removal on exact curves.
ConvertBeziersToEdges();

View File

@ -58,7 +58,7 @@ void CairoRenderer::OutputEnd() {
}
void CairoRenderer::SelectStroke(hStroke hcs) {
if(current.hcs.v == hcs.v) return;
if(current.hcs == hcs) return;
FinishPath();
Stroke *stroke = strokes.FindById(hcs);

View File

@ -249,7 +249,7 @@ void OpenGl1Renderer::UnSelectPrimitive() {
}
Canvas::Stroke *OpenGl1Renderer::SelectStroke(hStroke hcs) {
if(current.hcs.v == hcs.v) return current.stroke;
if(current.hcs == hcs) return current.stroke;
Stroke *stroke = strokes.FindById(hcs);
UnSelectPrimitive();
@ -270,7 +270,7 @@ Canvas::Stroke *OpenGl1Renderer::SelectStroke(hStroke hcs) {
}
Canvas::Fill *OpenGl1Renderer::SelectFill(hFill hcf) {
if(current.hcf.v == hcf.v) return current.fill;
if(current.hcf == hcf) return current.fill;
Fill *fill = fills.FindById(hcf);
UnSelectPrimitive();

View File

@ -195,7 +195,7 @@ static void ssglDepthRange(Canvas::Layer layer, int zIndex) {
//-----------------------------------------------------------------------------
Canvas::Stroke *OpenGl3Renderer::SelectStroke(hStroke hcs) {
if(current.hcs.v == hcs.v) return current.stroke;
if(current.hcs == hcs) return current.stroke;
Stroke *stroke = strokes.FindById(hcs);
ssglDepthRange(stroke->layer, stroke->zIndex);
@ -241,7 +241,7 @@ void OpenGl3Renderer::SelectMask(FillPattern pattern) {
}
Canvas::Fill *OpenGl3Renderer::SelectFill(hFill hcf) {
if(current.hcf.v == hcf.v) return current.fill;
if(current.hcf == hcf) return current.fill;
Fill *fill = fills.FindById(hcf);
ssglDepthRange(fill->layer, fill->zIndex);
@ -458,7 +458,8 @@ void OpenGl3Renderer::DrawEdges(const SEdgeList &el, hStroke hcs) {
}
void OpenGl3Renderer::DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs mode) {
if(ol.l.n == 0) return;
if(ol.l.IsEmpty())
return;
Stroke *stroke = SelectStroke(hcs);
ssassert(stroke->stipplePattern != StipplePattern::ZIGZAG &&
@ -726,8 +727,8 @@ public:
// Data
EdgeRenderer::Handle handle;
virtual Canvas::Layer GetLayer() const override { return stroke.layer; };
virtual int GetZIndex() const override { return stroke.zIndex; };
Canvas::Layer GetLayer() const override { return stroke.layer; }
int GetZIndex() const override { return stroke.zIndex; }
static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SEdgeList &el,
Canvas::Stroke *stroke) {
@ -756,8 +757,8 @@ public:
OutlineRenderer::Handle handle;
Canvas::DrawOutlinesAs drawAs;
virtual Canvas::Layer GetLayer() const override { return stroke.layer; };
virtual int GetZIndex() const override { return stroke.zIndex; };
Canvas::Layer GetLayer() const override { return stroke.layer; }
int GetZIndex() const override { return stroke.zIndex; }
static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SOutlineList &ol,
Canvas::Stroke *stroke,
@ -787,8 +788,8 @@ public:
// Data
IndexedMeshRenderer::Handle handle;
virtual Canvas::Layer GetLayer() const override { return stroke.layer; };
virtual int GetZIndex() const override { return stroke.zIndex; };
Canvas::Layer GetLayer() const override { return stroke.layer; }
int GetZIndex() const override { return stroke.zIndex; }
static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SIndexedMesh &mesh,
Canvas::Stroke *stroke) {
@ -816,8 +817,8 @@ public:
// Data
IndexedMeshRenderer::Handle handle;
virtual Canvas::Layer GetLayer() const override { return fill.layer; };
virtual int GetZIndex() const override { return fill.zIndex; };
Canvas::Layer GetLayer() const override { return fill.layer; }
int GetZIndex() const override { return fill.zIndex; }
static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SIndexedMesh &mesh,
Canvas::Fill *fill) {
@ -855,8 +856,8 @@ public:
bool hasFillBack;
bool isShaded;
virtual Canvas::Layer GetLayer() const override { return fillFront.layer; };
virtual int GetZIndex() const override { return fillFront.zIndex; };
Canvas::Layer GetLayer() const override { return fillFront.layer; }
int GetZIndex() const override { return fillFront.zIndex; }
static std::shared_ptr<DrawCall> Create(OpenGl3Renderer *renderer, const SMesh &m,
Canvas::Fill *fillFront, Canvas::Fill *fillBack = NULL,
@ -1020,7 +1021,7 @@ public:
void DrawMesh(const SMesh &m, hFill hcfFront, hFill hcfBack = {}) override {
drawCalls.emplace(MeshDrawCall::Create(renderer, m, fills.FindById(hcfFront),
fills.FindByIdNoOops(hcfBack),
/*lighting=*/true));
/*isShaded=*/true));
}
void DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces, hFill hcf) override {

View File

@ -146,7 +146,7 @@ void Request::Generate(IdList<Entity,hEntity> *entity,
p.group = group;
p.style = style;
p.construction = e.construction;
if(workplane.v == Entity::FREE_IN_3D.v) {
if(workplane == Entity::FREE_IN_3D) {
p.type = Entity::Type::POINT_IN_3D;
// params for x y z
p.param[0] = AddParam(param, h.param(16 + 3*i + 0));
@ -168,7 +168,7 @@ void Request::Generate(IdList<Entity,hEntity> *entity,
n.group = group;
n.style = style;
n.construction = e.construction;
if(workplane.v == Entity::FREE_IN_3D.v) {
if(workplane == Entity::FREE_IN_3D) {
n.type = Entity::Type::NORMAL_IN_3D;
n.param[0] = AddParam(param, h.param(32+0));
n.param[1] = AddParam(param, h.param(32+1));
@ -203,11 +203,11 @@ void Request::Generate(IdList<Entity,hEntity> *entity,
std::string Request::DescriptionString() const {
const char *s = "";
if(h.v == Request::HREQUEST_REFERENCE_XY.v) {
if(h == Request::HREQUEST_REFERENCE_XY) {
s = "#XY";
} else if(h.v == Request::HREQUEST_REFERENCE_YZ.v) {
} else if(h == Request::HREQUEST_REFERENCE_YZ) {
s = "#YZ";
} else if(h.v == Request::HREQUEST_REFERENCE_ZX.v) {
} else if(h == Request::HREQUEST_REFERENCE_ZX) {
s = "#ZX";
} else {
switch(type) {
@ -228,10 +228,10 @@ std::string Request::DescriptionString() const {
int Request::IndexOfPoint(hEntity he) const {
if(type == Type::DATUM_POINT) {
return (he.v == h.entity(0).v) ? 0 : -1;
return (he == h.entity(0)) ? 0 : -1;
}
for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
if(he.v == h.entity(i + 1).v) {
if(he == h.entity(i + 1)) {
return i;
}
}

View File

@ -540,7 +540,7 @@ void BitmapFont::AddGlyph(char32_t codepoint, std::shared_ptr<const Pixmap> pixm
BitmapFont::Glyph glyph = {};
glyph.advanceCells = (uint8_t)(pixmap->width / 8);
glyph.position = nextPosition++;
glyphs.emplace(codepoint, std::move(glyph));
glyphs.emplace(codepoint, glyph);
for(size_t y = 0; y < pixmap->height; y++) {
uint8_t *row = BitmapFontTextureRow(texture, glyph.position, y);
@ -623,7 +623,8 @@ const BitmapFont::Glyph &BitmapFont::GetGlyph(char32_t codepoint) {
}
}
it = glyphs.emplace(codepoint, std::move(glyph)).first;
it = glyphs.emplace(codepoint, glyph).first;
textureUpdated = true;
return (*it).second;
}
@ -1147,7 +1148,7 @@ PluralExpr::Token PluralExpr::Lex() {
}
PluralExpr::Token PluralExpr::PopToken() {
ssassert(stack.size() > 0, "Expected a non-empty stack");
ssassert(!stack.empty(), "Expected a non-empty stack");
Token t = stack.back();
stack.pop_back();
return t;
@ -1406,7 +1407,7 @@ void GettextParser::Parse() {
}
}
if(key.ident == "") {
if(key.ident.empty()) {
ssassert(msgstrs.size() == 1,
"Expected exactly one header msgstr");
ParseHeader(msgstrs[0]);

View File

@ -59,6 +59,10 @@ public:
inline hParam param(int i) const;
inline hEquation equation(int i) const;
};
template<>
struct IsHandleOracle<hGroup> : std::true_type {};
class hRequest {
public:
// bits 15: 0 -- request index
@ -69,6 +73,10 @@ public:
inline bool IsFromReferences() const;
};
template<>
struct IsHandleOracle<hRequest> : std::true_type {};
class hEntity {
public:
// bits 15: 0 -- entity index
@ -80,6 +88,10 @@ public:
inline hGroup group() const;
inline hEquation equation(int i) const;
};
template<>
struct IsHandleOracle<hEntity> : std::true_type {};
class hParam {
public:
// bits 15: 0 -- param index
@ -89,14 +101,24 @@ public:
inline hRequest request() const;
};
template<>
struct IsHandleOracle<hParam> : std::true_type {};
class hStyle {
public:
uint32_t v;
};
template<>
struct IsHandleOracle<hStyle> : std::true_type {};
struct EntityId {
uint32_t v; // entity ID, starting from 0
};
template<>
struct IsHandleOracle<EntityId> : std::true_type {};
struct EntityKey {
hEntity input;
int copyNumber;
@ -111,7 +133,7 @@ struct EntityKeyHash {
};
struct EntityKeyEqual {
bool operator()(const EntityKey &a, const EntityKey &b) const {
return std::tie(a.input.v, a.copyNumber) == std::tie(b.input.v, b.copyNumber);
return std::tie(a.input, a.copyNumber) == std::tie(b.input, b.copyNumber);
}
};
typedef std::unordered_map<EntityKey, EntityId, EntityKeyHash, EntityKeyEqual> EntityMap;
@ -129,6 +151,7 @@ public:
N_TRANS,
N_ROT_AA,
N_ROT_TRANS,
N_ROT_AXIS_TRANS,
};
enum class Type : uint32_t {
@ -136,6 +159,8 @@ public:
DRAWING_WORKPLANE = 5001,
EXTRUDE = 5100,
LATHE = 5101,
REVOLVE = 5102,
HELIX = 5103,
ROTATE = 5200,
TRANSLATE = 5201,
LINKED = 5300
@ -258,11 +283,12 @@ public:
hEntity Remap(hEntity in, int copyNumber);
void MakeExtrusionLines(EntityList *el, hEntity in);
void MakeLatheCircles(IdList<Entity,hEntity> *el, IdList<Param,hParam> *param, hEntity in, Vector pt, Vector axis, int ai);
void MakeLatheSurfacesSelectable(IdList<Entity, hEntity> *el, hEntity in, Vector axis);
void MakeExtrusionTopBottomFaces(EntityList *el, hEntity pt);
void CopyEntity(EntityList *el,
Entity *ep, int timesApplied, int remap,
hParam dx, hParam dy, hParam dz,
hParam qw, hParam qvx, hParam qvy, hParam qvz,
hParam qw, hParam qvx, hParam qvy, hParam qvz, hParam dist,
CopyAs as);
void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index);
@ -363,6 +389,7 @@ public:
POINT_N_ROT_TRANS = 2011,
POINT_N_COPY = 2012,
POINT_N_ROT_AA = 2013,
POINT_N_ROT_AXIS_TRANS = 2014,
NORMAL_IN_3D = 3000,
NORMAL_IN_2D = 3001,
@ -402,7 +429,7 @@ public:
hEntity distance;
// The only types that have their own params are points, normals,
// and directions.
hParam param[7];
hParam param[8];
// Transformed points/normals/distances have their numerical base
Vector numPoint;
@ -593,6 +620,9 @@ public:
inline hParam param(int i) const;
};
template<>
struct IsHandleOracle<hConstraint> : std::true_type {};
class ConstraintBase {
public:
int tag;
@ -660,10 +690,10 @@ public:
std::string comment; // since comments are represented as constraints
bool Equals(const ConstraintBase &c) const {
return type == c.type && group.v == c.group.v && workplane.v == c.workplane.v &&
valA == c.valA && valP.v == c.valP.v && ptA.v == c.ptA.v && ptB.v == c.ptB.v &&
entityA.v == c.entityA.v && entityB.v == c.entityB.v &&
entityC.v == c.entityC.v && entityD.v == c.entityD.v &&
return type == c.type && group == c.group && workplane == c.workplane &&
valA == c.valA && valP == c.valP && ptA == c.ptA && ptB == c.ptB &&
entityA == c.entityA && entityB == c.entityB &&
entityC == c.entityC && entityD == c.entityD &&
other == c.other && other2 == c.other2 && reference == c.reference &&
comment == c.comment;
}
@ -761,6 +791,9 @@ public:
inline hConstraint constraint() const;
};
template<>
struct IsHandleOracle<hEquation> : std::true_type {};
class Equation {
public:
int tag;
@ -887,9 +920,9 @@ inline hEquation hGroup::equation(int i) const
{ hEquation r; r.v = (v << 16) | 0x80000000 | (uint32_t)i; return r; }
inline bool hRequest::IsFromReferences() const {
if(v == Request::HREQUEST_REFERENCE_XY.v) return true;
if(v == Request::HREQUEST_REFERENCE_YZ.v) return true;
if(v == Request::HREQUEST_REFERENCE_ZX.v) return true;
if(*this == Request::HREQUEST_REFERENCE_XY) return true;
if(*this == Request::HREQUEST_REFERENCE_YZ) return true;
if(*this == Request::HREQUEST_REFERENCE_ZX) return true;
return false;
}
inline hEntity hRequest::entity(int i) const

View File

@ -772,70 +772,48 @@ void SolveSpaceUI::MenuAnalyze(Command id) {
}
case Command::VOLUME: {
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
Group *g = SK.GetGroup(SS.GW.activeGroup);
double totalVol = g->displayMesh.CalculateVolume();
std::string msg = ssprintf(
_("The volume of the solid model is:\n\n"
" %s"),
SS.MmToStringSI(totalVol, /*dim=*/3).c_str());
double vol = 0;
int i;
for(i = 0; i < m->l.n; i++) {
STriangle tr = m->l.elem[i];
// Translate to place vertex A at (x, y, 0)
Vector trans = Vector::From(tr.a.x, tr.a.y, 0);
tr.a = (tr.a).Minus(trans);
tr.b = (tr.b).Minus(trans);
tr.c = (tr.c).Minus(trans);
// Rotate to place vertex B on the y-axis. Depending on
// whether the triangle is CW or CCW, C is either to the
// right or to the left of the y-axis. This handles the
// sign of our normal.
Vector u = Vector::From(-tr.b.y, tr.b.x, 0);
u = u.WithMagnitude(1);
Vector v = Vector::From(tr.b.x, tr.b.y, 0);
v = v.WithMagnitude(1);
Vector n = Vector::From(0, 0, 1);
tr.a = (tr.a).DotInToCsys(u, v, n);
tr.b = (tr.b).DotInToCsys(u, v, n);
tr.c = (tr.c).DotInToCsys(u, v, n);
n = tr.Normal().WithMagnitude(1);
// Triangles on edge don't contribute
if(fabs(n.z) < LENGTH_EPS) continue;
// The plane has equation p dot n = a dot n
double d = (tr.a).Dot(n);
// nx*x + ny*y + nz*z = d
// nz*z = d - nx*x - ny*y
double A = -n.x/n.z, B = -n.y/n.z, C = d/n.z;
double mac = tr.c.y/tr.c.x, mbc = (tr.c.y - tr.b.y)/tr.c.x;
double xc = tr.c.x, yb = tr.b.y;
// I asked Maple for
// int(int(A*x + B*y +C, y=mac*x..(mbc*x + yb)), x=0..xc);
double integral =
(1.0/3)*(
A*(mbc-mac)+
(1.0/2)*B*(mbc*mbc-mac*mac)
)*(xc*xc*xc)+
(1.0/2)*(A*yb+B*yb*mbc+C*(mbc-mac))*xc*xc+
C*yb*xc+
(1.0/2)*B*yb*yb*xc;
vol += integral;
SMesh curMesh = {};
g->thisShell.TriangulateInto(&curMesh);
double curVol = curMesh.CalculateVolume();
if(curVol > 0.0) {
msg += ssprintf(
_("\nThe volume of current group mesh is:\n\n"
" %s"),
SS.MmToStringSI(curVol, /*dim=*/3).c_str());
}
Message(_("The volume of the solid model is:\n\n"
" %s\n\n"
"Curved surfaces have been approximated as triangles.\n"
"This introduces error, typically of around 1%%."),
SS.MmToStringSI(vol, /*dim=*/3).c_str());
msg += _("\n\nCurved surfaces have been approximated as triangles.\n"
"This introduces error, typically of around 1%.");
Message("%s", msg.c_str());
break;
}
case Command::AREA: {
Group *g = SK.GetGroup(SS.GW.activeGroup);
SS.GW.GroupSelection();
auto const &gs = SS.GW.gs;
double scale = SS.MmPerUnit();
if(gs.faces > 0) {
std::vector<uint32_t> faces;
faces.push_back(gs.face[0].v);
if(gs.faces > 1) faces.push_back(gs.face[1].v);
double area = g->displayMesh.CalculateSurfaceArea(faces);
Message(_("The surface area of the selected faces is:\n\n"
" %s\n\n"
"Curves have been approximated as piecewise linear.\n"
"This introduces error, typically of around 1%%."),
SS.MmToStringSI(area, /*dim=*/2).c_str());
break;
}
if(g->polyError.how != PolyError::GOOD) {
Error(_("This group does not contain a correctly-formed "
"2d closed area. It is open, not coplanar, or self-"
@ -907,7 +885,7 @@ void SolveSpaceUI::MenuAnalyze(Command id) {
int i;
SContour *sc = &(SS.traced.path);
for(i = 0; i < sc->l.n; i++) {
Vector p = sc->l.elem[i].p;
Vector p = sc->l[i].p;
double s = SS.exportScale;
fprintf(f, "%.10f, %.10f, %.10f\r\n",
p.x/s, p.y/s, p.z/s);
@ -938,7 +916,7 @@ void SolveSpaceUI::ShowNakedEdges(bool reportOnlyWhenNotOkay) {
root->MakeCertainEdgesInto(&(SS.nakedEdges),
EdgeKind::NAKED_OR_SELF_INTER, /*coplanarIsInter=*/true, &inters, &leaks);
if(reportOnlyWhenNotOkay && !inters && !leaks && SS.nakedEdges.l.n == 0) {
if(reportOnlyWhenNotOkay && !inters && !leaks && SS.nakedEdges.l.IsEmpty()) {
return;
}
SS.GW.Invalidate();
@ -954,7 +932,7 @@ void SolveSpaceUI::ShowNakedEdges(bool reportOnlyWhenNotOkay) {
_("\n\nThe model contains %d triangles, from %d surfaces."),
g->displayMesh.l.n, g->runningShell.surface.n);
if(SS.nakedEdges.l.n == 0) {
if(SS.nakedEdges.l.IsEmpty()) {
Message(_("%s\n\n%s\n\nZero problematic edges, good.%s"),
intersMsg, leaksMsg, cntMsg.c_str());
} else {
@ -1045,7 +1023,7 @@ BBox Sketch::CalculateEntityBBox(bool includingInvisible) {
// arc center point shouldn't be included in bounding box calculation
if(e.IsPoint() && e.h.isFromRequest()) {
Request *r = SK.GetRequest(e.h.request());
if(r->type == Request::Type::ARC_OF_CIRCLE && e.h.v == r->h.entity(1).v) {
if(r->type == Request::Type::ARC_OF_CIRCLE && e.h == r->h.entity(1)) {
continue;
}
}

View File

@ -165,13 +165,8 @@ enum class Unit : uint32_t {
METERS
};
template<class T>
struct CompareHandle {
bool operator()(T lhs, T rhs) const { return lhs.v < rhs.v; }
};
template<class Key, class T>
using handle_map = std::map<Key, T, CompareHandle<Key>>;
using handle_map = std::map<Key, T>;
class Group;
class SSurface;
@ -219,7 +214,7 @@ class ReadUTF8 {
public:
ReadUTF8(const std::string &str) : str(str) {}
utf8_iterator begin() const { return utf8_iterator(&str[0]); }
utf8_iterator end() const { return utf8_iterator(&str[str.length()]); }
utf8_iterator end() const { return utf8_iterator(&str[0] + str.length()); }
};
@ -703,6 +698,7 @@ public:
void ExportMeshAsObjTo(FILE *fObj, FILE *fMtl, SMesh *sm);
void ExportMeshAsThreeJsTo(FILE *f, const Platform::Path &filename,
SMesh *sm, SOutlineList *sol);
void ExportMeshAsVrmlTo(FILE *f, const Platform::Path &filename, SMesh *sm);
void ExportViewOrWireframeTo(const Platform::Path &filename, bool exportWireframe);
void ExportSectionTo(const Platform::Path &filename);
void ExportWireframeCurves(SEdgeList *sel, SBezierList *sbl,

View File

@ -24,17 +24,6 @@ void SShell::MakeFromDifferenceOf(SShell *a, SShell *b) {
// the intersection of srfA and srfB.) Return a new pwl curve with everything
// split.
//-----------------------------------------------------------------------------
static Vector LineStart, LineDirection;
static int ByTAlongLine(const void *av, const void *bv)
{
SInter *a = (SInter *)av,
*b = (SInter *)bv;
double ta = (a->p.Minus(LineStart)).DivPivoting(LineDirection),
tb = (b->p.Minus(LineStart)).DivPivoting(LineDirection);
return (ta > tb) ? 1 : -1;
}
SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
SSurface *srfA, SSurface *srfB) const
{
@ -59,7 +48,7 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
agnstB->AllPointsIntersecting(prev.p, p->p, &il,
/*asSegment=*/true, /*trimmed=*/false, /*inclTangent=*/true);
if(il.n > 0) {
if(!il.IsEmpty()) {
// The intersections were generated by intersecting the pwl
// edge against a surface; so they must be refined to lie
// exactly on the original curve.
@ -104,9 +93,14 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
// And now sort them in order along the line. Note that we must
// do that after refining, in case the refining would make two
// points switch places.
LineStart = prev.p;
LineDirection = (p->p).Minus(prev.p);
qsort(il.elem, il.n, sizeof(il.elem[0]), ByTAlongLine);
const Vector lineStart = prev.p;
const Vector lineDirection = (p->p).Minus(prev.p);
std::sort(il.begin(), il.end(), [&](const SInter &a, const SInter &b) {
double ta = (a.p.Minus(lineStart)).DivProjected(lineDirection);
double tb = (b.p.Minus(lineStart)).DivProjected(lineDirection);
return (ta < tb);
});
// And now uses the intersections to generate our split pwl edge(s)
Vector prev = Vector::From(VERY_POSITIVE, 0, 0);
@ -297,18 +291,18 @@ static void DEBUGEDGELIST(SEdgeList *sel, SSurface *surf) {
void SSurface::FindChainAvoiding(SEdgeList *src, SEdgeList *dest,
SPointList *avoid)
{
ssassert(src->l.n > 0, "Need at least one edge");
ssassert(!src->l.IsEmpty(), "Need at least one edge");
// Start with an arbitrary edge.
dest->l.Add(&(src->l.elem[0]));
dest->l.Add(src->l.First());
src->l.ClearTags();
src->l.elem[0].tag = 1;
src->l.First()->tag = 1;
bool added;
do {
added = false;
// The start and finish of the current edge chain
Vector s = dest->l.elem[0].a,
f = dest->l.elem[dest->l.n - 1].b;
Vector s = dest->l.First()->a,
f = dest->l.Last()->b;
// We can attach a new edge at the start or finish, as long as that
// start or finish point isn't in the list of points to avoid.
@ -444,15 +438,15 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
for(sc = into->curve.First(); sc; sc = into->curve.NextAfter(sc)) {
if(sc->source != SCurve::Source::INTERSECTION) continue;
if(opA) {
if(sc->surfA.v != h.v || sc->surfB.v != ss->h.v) continue;
if(sc->surfA != h || sc->surfB != ss->h) continue;
} else {
if(sc->surfB.v != h.v || sc->surfA.v != ss->h.v) continue;
if(sc->surfB != h || sc->surfA != ss->h) continue;
}
int i;
for(i = 1; i < sc->pts.n; i++) {
Vector a = sc->pts.elem[i-1].p,
b = sc->pts.elem[i].p;
Vector a = sc->pts[i-1].p,
b = sc->pts[i].p;
Point2d auv, buv;
ss->ClosestPointTo(a, &(auv.x), &(auv.y));
@ -512,13 +506,13 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
// our original and intersecting edge lists.
SEdgeList final = {};
while(orig.l.n > 0) {
while(!orig.l.IsEmpty()) {
SEdgeList chain = {};
FindChainAvoiding(&orig, &chain, &choosing);
// Arbitrarily choose an edge within the chain to classify; they
// should all be the same, though.
se = &(chain.l.elem[chain.l.n/2]);
se = &(chain.l[chain.l.n/2]);
Point2d auv = (se->a).ProjectXy(),
buv = (se->b).ProjectXy();
@ -546,12 +540,12 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
chain.Clear();
}
while(inter.l.n > 0) {
while(!inter.l.IsEmpty()) {
SEdgeList chain = {};
FindChainAvoiding(&inter, &chain, &choosing);
// Any edge in the chain, same as above.
se = &(chain.l.elem[chain.l.n/2]);
se = &(chain.l[chain.l.n/2]);
Point2d auv = (se->a).ProjectXy(),
buv = (se->b).ProjectXy();
@ -731,7 +725,7 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, SSurface::CombineAs type) {
a->MakeClassifyingBsps(this);
b->MakeClassifyingBsps(this);
if(b->surface.n == 0 || a->surface.n == 0) {
if(b->surface.IsEmpty() || a->surface.IsEmpty()) {
I = 1000000;
} else {
I = 0;
@ -775,19 +769,6 @@ SBspUv *SBspUv::Alloc() {
return (SBspUv *)AllocTemporary(sizeof(SBspUv));
}
static int ByLength(const void *av, const void *bv)
{
SEdge *a = (SEdge *)av,
*b = (SEdge *)bv;
double la = (a->a).Minus(a->b).Magnitude(),
lb = (b->a).Minus(b->b).Magnitude();
// Sort in descending order, longest first. This improves numerical
// stability for the normals.
return (la < lb) ? 1 : -1;
}
SBspUv *SBspUv::From(SEdgeList *el, SSurface *srf) {
SEdgeList work = {};
@ -795,8 +776,12 @@ SBspUv *SBspUv::From(SEdgeList *el, SSurface *srf) {
for(se = el->l.First(); se; se = el->l.NextAfter(se)) {
work.AddEdge(se->a, se->b, se->auxA, se->auxB);
}
qsort(work.l.elem, work.l.n, sizeof(work.l.elem[0]), ByLength);
std::sort(work.l.begin(), work.l.end(), [](SEdge const &a, SEdge const &b) {
double la = (a.a).Minus(a.b).Magnitude(), lb = (b.a).Minus(b.b).Magnitude();
// Sort in descending order, longest first. This improves numerical
// stability for the normals.
return la > lb;
});
SBspUv *bsp = NULL;
for(se = work.l.First(); se; se = work.l.NextAfter(se)) {
bsp = InsertOrCreateEdge(bsp, (se->a).ProjectXy(), (se->b).ProjectXy(), srf);

View File

@ -239,12 +239,12 @@ void SBezierList::CullIdenticalBeziers(bool both) {
l.ClearTags();
for(i = 0; i < l.n; i++) {
SBezier *bi = &(l.elem[i]), bir;
SBezier *bi = &(l[i]), bir;
bir = *bi;
bir.Reverse();
for(j = i + 1; j < l.n; j++) {
SBezier *bj = &(l.elem[j]);
SBezier *bj = &(l[j]);
if(bj->Equals(bi) ||
bj->Equals(&bir))
{
@ -311,8 +311,8 @@ bool SBezierList::GetPlaneContainingBeziers(Vector *p, Vector *u, Vector *v,
int i;
// Get any point on any Bezier; or an arbitrary point if list is empty.
if(l.n > 0) {
pt = l.elem[0].Start();
if(!l.IsEmpty()) {
pt = l[0].Start();
} else {
pt = Vector::From(0, 0, 0);
}
@ -393,7 +393,7 @@ SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl,
if(sbl->l.n < 1) return loop;
sbl->l.ClearTags();
SBezier *first = &(sbl->l.elem[0]);
SBezier *first = &(sbl->l[0]);
first->tag = 1;
loop.l.Add(first);
Vector start = first->Start();
@ -402,11 +402,11 @@ SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl,
sbl->l.RemoveTagged();
while(sbl->l.n > 0 && !hanging.Equals(start)) {
while(!sbl->l.IsEmpty() && !hanging.Equals(start)) {
int i;
bool foundNext = false;
for(i = 0; i < sbl->l.n; i++) {
SBezier *test = &(sbl->l.elem[i]);
SBezier *test = &(sbl->l[i]);
if((test->Finish()).Equals(hanging) && test->auxA == auxA) {
test->Reverse();
@ -470,15 +470,15 @@ void SBezierLoop::MakePwlInto(SContour *sc, double chordTol) const {
}
}
// Ensure that it's exactly closed, not just within a numerical tolerance.
if((sc->l.elem[sc->l.n - 1].p).Equals(sc->l.elem[0].p)) {
sc->l.elem[sc->l.n - 1] = sc->l.elem[0];
if((sc->l.Last()->p).Equals(sc->l.First()->p)) {
*sc->l.Last() = *sc->l.First();
}
}
bool SBezierLoop::IsClosed() const {
if(l.n < 1) return false;
Vector s = l.elem[0].Start(),
f = l.elem[l.n-1].Finish();
if(l.IsEmpty()) return false;
Vector s = l.First()->Start(),
f = l.Last()->Finish();
return s.Equals(f);
}
@ -497,7 +497,7 @@ SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly,
SBezierLoopSet ret = {};
*allClosed = true;
while(sbl->l.n > 0) {
while(!sbl->l.IsEmpty()) {
bool thisClosed;
SBezierLoop loop;
loop = SBezierLoop::FromCurves(sbl, &thisClosed, errorAt);
@ -512,7 +512,7 @@ SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly,
} else {
ret.l.Add(&loop);
poly->AddEmptyContour();
loop.MakePwlInto(&(poly->l.elem[poly->l.n-1]), chordTol);
loop.MakePwlInto(poly->l.Last(), chordTol);
}
}
@ -553,14 +553,14 @@ double SBezierLoopSet::SignedArea() {
void SBezierLoopSet::MakePwlInto(SPolygon *sp) const {
for(const SBezierLoop *sbl = l.First(); sbl; sbl = l.NextAfter(sbl)) {
sp->AddEmptyContour();
sbl->MakePwlInto(&(sp->l.elem[sp->l.n - 1]));
sbl->MakePwlInto(sp->l.Last());
}
}
void SBezierLoopSet::Clear() {
int i;
for(i = 0; i < l.n; i++) {
(l.elem[i]).Clear();
(l[i]).Clear();
}
l.Clear();
}
@ -618,7 +618,7 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
for(pt = sc->l.First(); pt; pt = sc->l.NextAfter(pt)) {
double u, v;
srfuv->ClosestPointTo(pt->p, &u, &v);
spuv.l.elem[spuv.l.n - 1].AddPoint(Vector::From(u, v, 0));
spuv.l.Last()->AddPoint(Vector::From(u, v, 0));
}
}
spuv.normal = Vector::From(0, 0, 1); // must be, since it's in xy plane now
@ -630,8 +630,8 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
// works for curved surfaces too (important for STEP export).
spuv.FixContourDirections();
for(i = 0; i < spuv.l.n; i++) {
SContour *contour = &(spuv.l.elem[i]);
SBezierLoop *bl = &(sbls.l.elem[i]);
SContour *contour = &(spuv.l[i]);
SBezierLoop *bl = &(sbls.l[i]);
if(contour->tag) {
// This contour got reversed in the polygon to make the directions
// consistent, so the same must be necessary for the Bezier loop.
@ -648,7 +648,7 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
while(loopsRemaining) {
loopsRemaining = false;
for(i = 0; i < sbls.l.n; i++) {
SBezierLoop *loop = &(sbls.l.elem[i]);
SBezierLoop *loop = &(sbls.l[i]);
if(loop->tag != OUTER_LOOP) continue;
// Check if this contour contains any outer loops; if it does, then
@ -656,12 +656,12 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
// will steal their holes, since their holes also lie inside this
// contour.
for(j = 0; j < sbls.l.n; j++) {
SBezierLoop *outer = &(sbls.l.elem[j]);
SBezierLoop *outer = &(sbls.l[j]);
if(i == j) continue;
if(outer->tag != OUTER_LOOP) continue;
Vector p = spuv.l.elem[j].AnyEdgeMidpoint();
if(spuv.l.elem[i].ContainsPointProjdToNormal(spuv.normal, p)) {
Vector p = spuv.l[j].AnyEdgeMidpoint();
if(spuv.l[i].ContainsPointProjdToNormal(spuv.normal, p)) {
break;
}
}
@ -675,16 +675,16 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
loop->tag = USED_LOOP;
outerAndInners.l.Add(loop);
int auxA = 0;
if(loop->l.n > 0) auxA = loop->l.elem[0].auxA;
if(loop->l.n > 0) auxA = loop->l[0].auxA;
for(j = 0; j < sbls.l.n; j++) {
SBezierLoop *inner = &(sbls.l.elem[j]);
SBezierLoop *inner = &(sbls.l[j]);
if(inner->tag != INNER_LOOP) continue;
if(inner->l.n < 1) continue;
if(inner->l.elem[0].auxA != auxA) continue;
if(inner->l[0].auxA != auxA) continue;
Vector p = spuv.l.elem[j].AnyEdgeMidpoint();
if(spuv.l.elem[i].ContainsPointProjdToNormal(spuv.normal, p)) {
Vector p = spuv.l[j].AnyEdgeMidpoint();
if(spuv.l[i].ContainsPointProjdToNormal(spuv.normal, p)) {
outerAndInners.l.Add(inner);
inner->tag = USED_LOOP;
}
@ -702,7 +702,7 @@ void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz,
// to screw up on that stuff. So just add them onto the open curve list.
// Very ugly, but better than losing curves.
for(i = 0; i < sbls.l.n; i++) {
SBezierLoop *loop = &(sbls.l.elem[i]);
SBezierLoop *loop = &(sbls.l[i]);
if(loop->tag == USED_LOOP) continue;
if(openContours) {
@ -804,11 +804,11 @@ void SCurve::RemoveShortSegments(SSurface *srfA, SSurface *srfB) {
if(pts.n <= 3) return;
pts.ClearTags();
Vector prev = pts.elem[0].p;
Vector prev = pts[0].p;
int i, a;
for(i = 1; i < pts.n - 1; i++) {
SCurvePt *sct = &(pts.elem[i]),
*scn = &(pts.elem[i+1]);
SCurvePt *sct = &(pts[i]),
*scn = &(pts[i+1]);
if(sct->vertex) {
prev = sct->p;
continue;
@ -849,12 +849,12 @@ STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool backwards) {
SCurve *sc = shell->curve.FindById(hsc);
if(backwards) {
stb.finish = sc->pts.elem[0].p;
stb.start = sc->pts.elem[sc->pts.n - 1].p;
stb.finish = sc->pts[0].p;
stb.start = sc->pts.Last()->p;
stb.backwards = true;
} else {
stb.start = sc->pts.elem[0].p;
stb.finish = sc->pts.elem[sc->pts.n - 1].p;
stb.start = sc->pts[0].p;
stb.finish = sc->pts.Last()->p;
stb.backwards = false;
}

View File

@ -13,11 +13,12 @@ void SShell::MergeCoincidentSurfaces() {
SSurface *si, *sj;
for(i = 0; i < surface.n; i++) {
si = &(surface.elem[i]);
si = &(surface[i]);
if(si->tag) continue;
// Let someone else clean up the empty surfaces; we can certainly merge
// them, but we don't know how to calculate a reasonable bounding box.
if(si->trim.n == 0) continue;
if(si->trim.IsEmpty())
continue;
// And for now we handle only coincident planes, so no sense wasting
// time on other surfaces.
if(si->degm != 1 || si->degn != 1) continue;
@ -30,7 +31,7 @@ void SShell::MergeCoincidentSurfaces() {
mergedThisTime = false;
for(j = i + 1; j < surface.n; j++) {
sj = &(surface.elem[j]);
sj = &(surface[j]);
if(sj->tag) continue;
if(!sj->CoincidentWith(si, /*sameNormal=*/true)) continue;
if(!sj->color.Equals(si->color)) continue;
@ -59,8 +60,8 @@ void SShell::MergeCoincidentSurfaces() {
// new srf
SCurve *sc;
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
if(sc->surfA.v == sj->h.v) sc->surfA = si->h;
if(sc->surfB.v == sj->h.v) sc->surfB = si->h;
if(sc->surfA == sj->h) sc->surfA = si->h;
if(sc->surfB == sj->h) sc->surfB = si->h;
}
}

View File

@ -155,7 +155,7 @@ void SBezier::ClosestPointTo(Vector p, double *t, bool mustConverge) const {
Vector dp = TangentAt(*t);
Vector pc = p.ClosestPointOnLine(p0, dp);
*t += (pc.Minus(p0)).DivPivoting(dp);
*t += (pc.Minus(p0)).DivProjected(dp);
}
if(mustConverge) {
dbp("didn't converge (closest point on bezier curve)");
@ -231,7 +231,7 @@ void SBezier::MakePwlInto(SEdgeList *sel, double chordTol) const {
MakePwlInto(&lv, chordTol);
int i;
for(i = 1; i < lv.n; i++) {
sel->AddEdge(lv.elem[i-1], lv.elem[i]);
sel->AddEdge(lv[i-1], lv[i]);
}
lv.Clear();
}
@ -242,7 +242,7 @@ void SBezier::MakePwlInto(List<SCurvePt> *l, double chordTol) const {
for(i = 0; i < lv.n; i++) {
SCurvePt scpt;
scpt.tag = 0;
scpt.p = lv.elem[i];
scpt.p = lv[i];
scpt.vertex = (i == 0) || (i == (lv.n - 1));
l->Add(&scpt);
}
@ -253,7 +253,7 @@ void SBezier::MakePwlInto(SContour *sc, double chordTol) const {
MakePwlInto(&lv, chordTol);
int i;
for(i = 0; i < lv.n; i++) {
sc->AddPoint(lv.elem[i]);
sc->AddPoint(lv[i]);
}
lv.Clear();
}

View File

@ -311,7 +311,7 @@ void SSurface::AllPointsIntersecting(Vector a, Vector b,
}
int i;
for(i = 0; i < ip_n; i++) {
double t = (ip[i].Minus(ap)).DivPivoting(bp.Minus(ap));
double t = (ip[i].Minus(ap)).DivProjected(bp.Minus(ap));
// This is a point on the circle; but is it on the arc?
Point2d pp = ap.Plus((bp.Minus(ap)).ScaledBy(t));
double theta = atan2(pp.y, pp.x);
@ -342,19 +342,19 @@ void SSurface::AllPointsIntersecting(Vector a, Vector b,
int i, j;
for(i = 0; i < inters.n; i++) {
for(j = i + 1; j < inters.n; j++) {
if(inters.elem[i].p.Equals(inters.elem[j].p)) {
inters.elem[j].tag = 1;
if(inters[i].p.Equals(inters[j].p)) {
inters[j].tag = 1;
}
}
}
inters.RemoveTagged();
for(i = 0; i < inters.n; i++) {
Point2d puv = inters.elem[i].p;
Point2d puv = inters[i].p;
// Make sure the point lies within the finite line segment
Vector pxyz = PointAt(puv.x, puv.y);
double t = (pxyz.Minus(a)).DivPivoting(ba);
double t = (pxyz.Minus(a)).DivProjected(ba);
if(asSegment && (t > 1 - LENGTH_EPS/bam || t < LENGTH_EPS/bam)) {
continue;
}
@ -463,7 +463,7 @@ bool SShell::ClassifyEdge(Class *indir, Class *outdir,
}
if(edge_inters == 2) {
// TODO, make this use the appropriate curved normals
//! @todo make this use the appropriate curved normals
double dotp[2];
for(int i = 0; i < 2; i++) {
dotp[i] = edge_n_out.DirectionCosineWith(inter_surf_n[i]);
@ -566,7 +566,7 @@ bool SShell::ClassifyEdge(Class *indir, Class *outdir,
SInter *si;
for(si = l.First(); si; si = l.NextAfter(si)) {
double t = ((si->p).Minus(p)).DivPivoting(ray);
double t = ((si->p).Minus(p)).DivProjected(ray);
if(t*ray.Magnitude() < -LENGTH_EPS) {
// wrong side, doesn't count
continue;

View File

@ -63,18 +63,19 @@ bool SSurface::IsCylinder(Vector *axis, Vector *center, double *r,
return true;
}
SSurface SSurface::FromRevolutionOf(SBezier *sb, Vector pt, Vector axis,
double thetas, double thetaf)
{
// Create a surface patch by revolving and possibly translating a curve.
// Works for sections up to but not including 180 degrees.
SSurface SSurface::FromRevolutionOf(SBezier *sb, Vector pt, Vector axis, double thetas,
double thetaf, double dists,
double distf) { // s is start, f is finish
SSurface ret = {};
ret.degm = sb->deg;
ret.degn = 2;
double dtheta = fabs(WRAP_SYMMETRIC(thetaf - thetas, 2*PI));
double w = cos(dtheta / 2);
// We now wish to revolve the curve about the z axis
// Revolve the curve about the z axis
int i;
for(i = 0; i <= ret.degm; i++) {
Vector p = sb->ctrl[i];
@ -82,32 +83,26 @@ SSurface SSurface::FromRevolutionOf(SBezier *sb, Vector pt, Vector axis,
Vector ps = p.RotatedAbout(pt, axis, thetas),
pf = p.RotatedAbout(pt, axis, thetaf);
Vector ct;
// The middle control point should be at the intersection of the tangents at ps and pf.
// This is equivalent but works for 0 <= angle < 180 degrees.
Vector mid = ps.Plus(pf).ScaledBy(0.5);
Vector c = ps.ClosestPointOnLine(pt, axis);
Vector ct = mid.Minus(c).ScaledBy(1 / (w * w)).Plus(c);
// not sure this is needed
if(ps.Equals(pf)) {
// Degenerate case: a control point lies on the axis of revolution,
// so we get three coincident control points.
ct = ps;
} else {
// Normal case, the control point sweeps out a circle.
Vector c = ps.ClosestPointOnLine(pt, axis);
Vector rs = ps.Minus(c),
rf = pf.Minus(c);
Vector ts = axis.Cross(rs),
tf = axis.Cross(rf);
ct = Vector::AtIntersectionOfLines(ps, ps.Plus(ts),
pf, pf.Plus(tf),
NULL, NULL, NULL);
ps = c;
ct = c;
pf = c;
}
ret.ctrl[i][0] = ps;
ret.ctrl[i][1] = ct;
ret.ctrl[i][2] = pf;
// moving along the axis can create hilical surfaces (or straight extrusion if
// thetas==thetaf)
ret.ctrl[i][0] = ps.Plus(axis.ScaledBy(dists));
ret.ctrl[i][1] = ct.Plus(axis.ScaledBy((dists + distf) / 2));
ret.ctrl[i][2] = pf.Plus(axis.ScaledBy(distf));
ret.weight[i][0] = sb->weight[i];
ret.weight[i][1] = sb->weight[i]*cos(dtheta/2);
ret.weight[i][1] = sb->weight[i] * w;
ret.weight[i][2] = sb->weight[i];
}
@ -240,7 +235,7 @@ void SSurface::MakeTrimEdgesInto(SEdgeList *sel, MakeAs flags,
increment = 1;
}
for(i = first; i != (last + increment); i += increment) {
Vector tpt, *pt = &(sc->pts.elem[i].p);
Vector tpt, *pt = &(sc->pts[i].p);
if(flags == MakeAs::UV) {
ClosestPointTo(*pt, &u, &v);
@ -348,11 +343,11 @@ void SSurface::MakeSectionEdgesInto(SShell *shell, SEdgeList *sel, SBezierList *
int sp, fp;
for(sp = 0; sp < sc->pts.n; sp++) {
if(s.Equals(sc->pts.elem[sp].p)) break;
if(s.Equals(sc->pts[sp].p)) break;
}
if(sp >= sc->pts.n) return;
for(fp = sp; fp < sc->pts.n; fp++) {
if(f.Equals(sc->pts.elem[fp].p)) break;
if(f.Equals(sc->pts[fp].p)) break;
}
if(fp >= sc->pts.n) return;
// So now the curve we want goes from elem[sp] to elem[fp]
@ -365,8 +360,8 @@ void SSurface::MakeSectionEdgesInto(SShell *shell, SEdgeList *sel, SBezierList *
for(;;) {
// So construct a cubic Bezier with the correct endpoints
// and tangents for the current span.
Vector st = sc->pts.elem[sp].p,
ft = sc->pts.elem[fpt].p,
Vector st = sc->pts[sp].p,
ft = sc->pts[fpt].p,
sf = ft.Minus(st);
double m = sf.Magnitude() / 3;
@ -383,7 +378,7 @@ void SSurface::MakeSectionEdgesInto(SShell *shell, SEdgeList *sel, SBezierList *
int i;
bool tooFar = false;
for(i = sp + 1; i <= (fpt - 1); i++) {
Vector p = sc->pts.elem[i].p;
Vector p = sc->pts[i].p;
double t;
sb.ClosestPointTo(p, &t, /*mustConverge=*/false);
Vector pp = sb.PointAt(t);
@ -440,7 +435,7 @@ void SSurface::TriangulateInto(SShell *shell, SMesh *sm) {
STriMeta meta = { face, color };
for(i = start; i < sm->l.n; i++) {
STriangle *st = &(sm->l.elem[i]);
STriangle *st = &(sm->l[i]);
st->meta = meta;
st->an = NormalAt(st->a.x, st->a.y);
st->bn = NormalAt(st->b.x, st->b.y);
@ -593,10 +588,10 @@ void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, Rgb
int i;
for(i = 0; i < trimLines.n; i++) {
TrimLine *tl = &(trimLines.elem[i]);
TrimLine *tl = &(trimLines[i]);
SSurface *ss = surface.FindById(tl->hs);
TrimLine *tlp = &(trimLines.elem[WRAP(i-1, trimLines.n)]);
TrimLine *tlp = &(trimLines[WRAP(i-1, trimLines.n)]);
STrimBy stb;
stb = STrimBy::EntireCurve(this, tl->hc, /*backwards=*/true);
@ -611,20 +606,11 @@ void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, Rgb
}
}
typedef struct {
hSSurface d[4];
} Revolved;
void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbaColor color, Group *group)
bool SShell::CheckNormalAxisRelationship(SBezierLoopSet *sbls, Vector pt, Vector axis, double da, double dx)
// Check that the direction of revolution/extrusion ends up parallel to the normal of
// the sketch, on the side of the axis where the sketch is.
{
SBezierLoop *sbl;
int i0 = surface.n, i;
// Normalize the axis direction so that the direction of revolution
// ends up parallel to the normal of the sketch, on the side of the
// axis where the sketch is.
Vector pto;
double md = VERY_NEGATIVE;
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
@ -634,80 +620,151 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
// if we choose a point that lies on the axis, for example.
// (And our surface will be self-intersecting if the sketch
// spans the axis, so don't worry about that.)
for(i = 0; i <= sb->deg; i++) {
for(int i = 0; i <= sb->deg; i++) {
Vector p = sb->ctrl[i];
double d = p.DistanceToLine(pt, axis);
if(d > md) {
md = d;
md = d;
pto = p;
}
}
}
}
Vector ptc = pto.ClosestPointOnLine(pt, axis),
up = (pto.Minus(ptc)).WithMagnitude(1),
vp = (sbls->normal).Cross(up);
if(vp.Dot(axis) < 0) {
axis = axis.ScaledBy(-1);
up = axis.Cross(pto.Minus(ptc)).ScaledBy(da),
vp = up.Plus(axis.ScaledBy(dx));
return (vp.Dot(sbls->normal) > 0);
}
// sketch must not contain the axis of revolution as a non-construction line for helix
void SShell::MakeFromHelicalRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
RgbaColor color, Group *group, double angles,
double anglef, double dists, double distf) {
int i0 = surface.n; // number of pre-existing surfaces
SBezierLoop *sbl;
// for testing - hard code the axial distance, and number of sections.
// distance will need to be parameters in the future.
double dist = distf - dists;
int sections = fabs(anglef - angles) / (PI / 2) + 1;
double wedge = (anglef - angles) / sections;
if(CheckNormalAxisRelationship(sbls, pt, axis, anglef-angles, distf-dists)) {
swap(angles, anglef);
swap(dists, distf);
dist = -dist;
wedge = -wedge;
}
// Now we actually build and trim the surfaces.
// Define a coordinate system to contain the original sketch, and get
// a bounding box in that csys
Vector n = sbls->normal.ScaledBy(-1);
Vector u = n.Normal(0), v = n.Normal(1);
Vector orig = sbls->point;
double umax = 1e-10, umin = 1e10;
sbls->GetBoundingProjd(u, orig, &umin, &umax);
double vmax = 1e-10, vmin = 1e10;
sbls->GetBoundingProjd(v, orig, &vmin, &vmax);
// and now fix things up so that all u and v lie between 0 and 1
orig = orig.Plus(u.ScaledBy(umin));
orig = orig.Plus(v.ScaledBy(vmin));
u = u.ScaledBy(umax - umin);
v = v.ScaledBy(vmax - vmin);
// So we can now generate the end caps of the extrusion within
// a translated and rotated (and maybe mirrored) version of that csys.
SSurface s0, s1;
s0 = SSurface::FromPlane(orig.RotatedAbout(pt, axis, angles).Plus(axis.ScaledBy(dists)),
u.RotatedAbout(axis, angles), v.RotatedAbout(axis, angles));
s0.color = color;
s1 = SSurface::FromPlane(
orig.Plus(u).RotatedAbout(pt, axis, anglef).Plus(axis.ScaledBy(distf)),
u.ScaledBy(-1).RotatedAbout(axis, anglef), v.RotatedAbout(axis, anglef));
s1.color = color;
hSSurface hs0 = surface.AddAndAssignId(&s0), hs1 = surface.AddAndAssignId(&s1);
// Now we actually build and trim the swept surfaces. One loop at a time.
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
int i, j;
SBezier *sb;
List<Revolved> hsl = {};
List<std::vector<hSSurface>> hsl = {};
// This is where all the NURBS are created and Remapped to the generating curve
for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
Revolved revs;
for(j = 0; j < 4; j++) {
if(sb->deg == 1 &&
(sb->ctrl[0]).DistanceToLine(pt, axis) < LENGTH_EPS &&
(sb->ctrl[1]).DistanceToLine(pt, axis) < LENGTH_EPS)
{
std::vector<hSSurface> revs(sections);
for(j = 0; j < sections; j++) {
if((dist == 0) && sb->deg == 1 &&
(sb->ctrl[0]).DistanceToLine(pt, axis) < LENGTH_EPS &&
(sb->ctrl[1]).DistanceToLine(pt, axis) < LENGTH_EPS) {
// This is a line on the axis of revolution; it does
// not contribute a surface.
revs.d[j].v = 0;
revs[j].v = 0;
} else {
SSurface ss = SSurface::FromRevolutionOf(sb, pt, axis,
(PI/2)*j,
(PI/2)*(j+1));
SSurface ss = SSurface::FromRevolutionOf(
sb, pt, axis, angles + (wedge)*j, angles + (wedge) * (j + 1),
dists + j * dist / sections, dists + (j + 1) * dist / sections);
ss.color = color;
if(sb->entity != 0) {
hEntity he;
he.v = sb->entity;
he.v = sb->entity;
hEntity hface = group->Remap(he, Group::REMAP_LINE_TO_FACE);
if(SK.entity.FindByIdNoOops(hface) != NULL) {
ss.face = hface.v;
}
}
revs.d[j] = surface.AddAndAssignId(&ss);
revs[j] = surface.AddAndAssignId(&ss);
}
}
hsl.Add(&revs);
}
// Still the same loop. Need to create trim curves
for(i = 0; i < sbl->l.n; i++) {
Revolved revs = hsl.elem[i],
revsp = hsl.elem[WRAP(i-1, sbl->l.n)];
std::vector<hSSurface> revs = hsl[i], revsp = hsl[WRAP(i - 1, sbl->l.n)];
sb = &(sbl->l.elem[i]);
sb = &(sbl->l[i]);
for(j = 0; j < 4; j++) {
// we generate one more curve than we did surfaces
for(j = 0; j <= sections; j++) {
SCurve sc;
Quaternion qs = Quaternion::From(axis, (PI/2)*j);
Quaternion qs = Quaternion::From(axis, angles + wedge * j);
// we want Q*(x - p) + p = Q*x + (p - Q*p)
Vector ts = pt.Minus(qs.Rotate(pt));
Vector ts =
pt.Minus(qs.Rotate(pt)).Plus(axis.ScaledBy(dists + j * dist / sections));
// If this input curve generate a surface, then trim that
// If this input curve generated a surface, then trim that
// surface with the rotated version of the input curve.
if(revs.d[j].v) {
sc = {};
if(revs[0].v) { // not d[j] because crash on j==sections
sc = {};
sc.isExact = true;
sc.exact = sb->TransformedBy(ts, qs, 1.0);
sc.exact = sb->TransformedBy(ts, qs, 1.0);
(sc.exact).MakePwlInto(&(sc.pts));
sc.surfA = revs.d[j];
sc.surfB = revs.d[WRAP(j-1, 4)];
// the surfaces already exist so trim with this curve
if(j < sections) {
sc.surfA = revs[j];
} else {
sc.surfA = hs1; // end cap
}
if(j > 0) {
sc.surfB = revs[j - 1];
} else {
sc.surfB = hs0; // staring cap
}
hSCurve hcb = curve.AddAndAssignId(&sc);
STrimBy stb;
stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/true);
(surface.FindById(sc.surfA))->trim.Add(&stb);
stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/false);
(surface.FindById(sc.surfB))->trim.Add(&stb);
} else if(j == 0) { // curve was on the rotation axis and is shared by the end caps.
sc = {};
sc.isExact = true;
sc.exact = sb->TransformedBy(ts, qs, 1.0);
(sc.exact).MakePwlInto(&(sc.pts));
sc.surfA = hs1; // end cap
sc.surfB = hs0; // staring cap
hSCurve hcb = curve.AddAndAssignId(&sc);
STrimBy stb;
@ -719,19 +776,17 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
// And if this input curve and the one after it both generated
// surfaces, then trim both of those by the appropriate
// circle.
if(revs.d[j].v && revsp.d[j].v) {
SSurface *ss = surface.FindById(revs.d[j]);
// curve based on the control points.
if((j < sections) && revs[j].v && revsp[j].v) {
SSurface *ss = surface.FindById(revs[j]);
sc = {};
sc = {};
sc.isExact = true;
sc.exact = SBezier::From(ss->ctrl[0][0],
ss->ctrl[0][1],
ss->ctrl[0][2]);
sc.exact = SBezier::From(ss->ctrl[0][0], ss->ctrl[0][1], ss->ctrl[0][2]);
sc.exact.weight[1] = ss->weight[0][1];
(sc.exact).MakePwlInto(&(sc.pts));
sc.surfA = revs.d[j];
sc.surfB = revsp.d[j];
sc.surfA = revs[j];
sc.surfB = revsp[j];
hSCurve hcc = curve.AddAndAssignId(&sc);
@ -747,8 +802,123 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
hsl.Clear();
}
if(dist == 0) {
MakeFirstOrderRevolvedSurfaces(pt, axis, i0);
}
}
void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbaColor color,
Group *group) {
int i0 = surface.n; // number of pre-existing surfaces
SBezierLoop *sbl;
if(CheckNormalAxisRelationship(sbls, pt, axis, 1.0, 0.0)) {
axis = axis.ScaledBy(-1);
}
// Now we actually build and trim the surfaces.
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
int i, j;
SBezier *sb;
List<std::vector<hSSurface>> hsl = {};
for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
std::vector<hSSurface> revs(4);
for(j = 0; j < 4; j++) {
if(sb->deg == 1 &&
(sb->ctrl[0]).DistanceToLine(pt, axis) < LENGTH_EPS &&
(sb->ctrl[1]).DistanceToLine(pt, axis) < LENGTH_EPS)
{
// This is a line on the axis of revolution; it does
// not contribute a surface.
revs[j].v = 0;
} else {
SSurface ss = SSurface::FromRevolutionOf(sb, pt, axis, (PI / 2) * j,
(PI / 2) * (j + 1), 0.0, 0.0);
ss.color = color;
if(sb->entity != 0) {
hEntity he;
he.v = sb->entity;
hEntity hface = group->Remap(he, Group::REMAP_LINE_TO_FACE);
if(SK.entity.FindByIdNoOops(hface) != NULL) {
ss.face = hface.v;
}
}
revs[j] = surface.AddAndAssignId(&ss);
}
}
hsl.Add(&revs);
}
for(i = 0; i < sbl->l.n; i++) {
std::vector<hSSurface> revs = hsl[i],
revsp = hsl[WRAP(i-1, sbl->l.n)];
sb = &(sbl->l[i]);
for(j = 0; j < 4; j++) {
SCurve sc;
Quaternion qs = Quaternion::From(axis, (PI/2)*j);
// we want Q*(x - p) + p = Q*x + (p - Q*p)
Vector ts = pt.Minus(qs.Rotate(pt));
// If this input curve generate a surface, then trim that
// surface with the rotated version of the input curve.
if(revs[j].v) {
sc = {};
sc.isExact = true;
sc.exact = sb->TransformedBy(ts, qs, 1.0);
(sc.exact).MakePwlInto(&(sc.pts));
sc.surfA = revs[j];
sc.surfB = revs[WRAP(j-1, 4)];
hSCurve hcb = curve.AddAndAssignId(&sc);
STrimBy stb;
stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/true);
(surface.FindById(sc.surfA))->trim.Add(&stb);
stb = STrimBy::EntireCurve(this, hcb, /*backwards=*/false);
(surface.FindById(sc.surfB))->trim.Add(&stb);
}
// And if this input curve and the one after it both generated
// surfaces, then trim both of those by the appropriate
// circle.
if(revs[j].v && revsp[j].v) {
SSurface *ss = surface.FindById(revs[j]);
sc = {};
sc.isExact = true;
sc.exact = SBezier::From(ss->ctrl[0][0],
ss->ctrl[0][1],
ss->ctrl[0][2]);
sc.exact.weight[1] = ss->weight[0][1];
(sc.exact).MakePwlInto(&(sc.pts));
sc.surfA = revs[j];
sc.surfB = revsp[j];
hSCurve hcc = curve.AddAndAssignId(&sc);
STrimBy stb;
stb = STrimBy::EntireCurve(this, hcc, /*backwards=*/false);
(surface.FindById(sc.surfA))->trim.Add(&stb);
stb = STrimBy::EntireCurve(this, hcc, /*backwards=*/true);
(surface.FindById(sc.surfB))->trim.Add(&stb);
}
}
}
hsl.Clear();
}
MakeFirstOrderRevolvedSurfaces(pt, axis, i0);
}
void SShell::MakeFirstOrderRevolvedSurfaces(Vector pt, Vector axis, int i0) {
int i;
for(i = i0; i < surface.n; i++) {
SSurface *srf = &(surface.elem[i]);
SSurface *srf = &(surface[i]);
// Revolution of a line; this is potentially a plane, which we can
// rewrite to have degree (1, 1).
@ -823,9 +993,7 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
continue;
}
}
}
}
void SShell::MakeFromCopyOf(SShell *a) {
@ -880,7 +1048,7 @@ void SShell::TriangulateInto(SMesh *sm) {
}
bool SShell::IsEmpty() const {
return (surface.n == 0);
return surface.IsEmpty();
}
void SShell::Clear() {
@ -896,4 +1064,3 @@ void SShell::Clear() {
}
curve.Clear();
}

View File

@ -63,11 +63,17 @@ public:
uint32_t v;
};
template<>
struct IsHandleOracle<hSSurface> : std::true_type {};
class hSCurve {
public:
uint32_t v;
};
template<>
struct IsHandleOracle<hSCurve> : std::true_type {};
// Stuff for rational polynomial curves, of degree one to three. These are
// our inputs, and are also calculated for certain exact surface-surface
// intersections.
@ -282,8 +288,8 @@ public:
Point2d cached;
static SSurface FromExtrusionOf(SBezier *spc, Vector t0, Vector t1);
static SSurface FromRevolutionOf(SBezier *sb, Vector pt, Vector axis,
double thetas, double thetaf);
static SSurface FromRevolutionOf(SBezier *sb, Vector pt, Vector axis, double thetas,
double thetaf, double dists, double distf);
static SSurface FromPlane(Vector pt, Vector u, Vector v);
static SSurface FromTransformationOf(SSurface *a, Vector t, Quaternion q,
double scale,
@ -376,9 +382,12 @@ public:
void MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1,
RgbaColor color);
bool CheckNormalAxisRelationship(SBezierLoopSet *sbls, Vector pt, Vector axis, double da, double dx);
void MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
RgbaColor color, Group *group);
void MakeFromHelicalRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, RgbaColor color,
Group *group, double angles, double anglef, double dists, double distf);
void MakeFirstOrderRevolvedSurfaces(Vector pt, Vector axis, int i0);
void MakeFromUnionOf(SShell *a, SShell *b);
void MakeFromDifferenceOf(SShell *a, SShell *b);
void MakeFromBoolean(SShell *a, SShell *b, SSurface::CombineAs type);

View File

@ -277,7 +277,7 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
int i;
for(i = 0; i < lv.n - 1; i++) {
Vector pa = lv.elem[i], pb = lv.elem[i+1];
Vector pa = lv[i], pb = lv[i+1];
pa = pa.Minus(axis.ScaledBy(pa.Dot(axis)));
pb = pb.Minus(axis.ScaledBy(pb.Dot(axis)));
pa = pa.Plus(axisc);
@ -326,12 +326,13 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
srfB->AllPointsIntersecting(se->a, se->b, &lsi,
/*asSegment=*/true, /*trimmed=*/true, /*inclTangent=*/false);
if(lsi.n == 0) continue;
if(lsi.IsEmpty())
continue;
// Find the other surface that this curve trims.
hSCurve hsc = { (uint32_t)se->auxA };
SCurve *sc = shA->curve.FindById(hsc);
hSSurface hother = (sc->surfA.v == srfA->h.v) ?
hSSurface hother = (sc->surfA == srfA->h) ?
sc->surfB : sc->surfA;
SSurface *other = shA->surface.FindById(hother);
@ -368,10 +369,10 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
sc.isExact = false;
sc.source = SCurve::Source::INTERSECTION;
Vector start = spl.l.elem[0].p,
startv = spl.l.elem[0].auxv;
Vector start = spl.l[0].p,
startv = spl.l[0].auxv;
spl.l.ClearTags();
spl.l.elem[0].tag = 1;
spl.l[0].tag = 1;
spl.l.RemoveTagged();
// Our chord tolerance is whatever the user specified

View File

@ -36,7 +36,7 @@ void SPolygon::UvTriangulateInto(SMesh *m, SSurface *srf) {
SContour merged = {};
top->tag = 1;
top->CopyInto(&merged);
(merged.l.n)--;
merged.l.RemoveLast(1);
// List all of the edges, for testing whether bridges work.
SEdgeList el = {};
@ -114,7 +114,7 @@ bool SContour::BridgeToContour(SContour *sc,
// point.
int sco = 0;
for(i = 0; i < (sc->l.n - 1); i++) {
if((sc->l.elem[i].p).EqualsExactly(sc->xminPt)) {
if((sc->l[i].p).EqualsExactly(sc->xminPt)) {
sco = i;
}
}
@ -124,7 +124,7 @@ bool SContour::BridgeToContour(SContour *sc,
int thiso = 0;
double dmin = 1e10;
for(i = 0; i < l.n; i++) {
Vector p = l.elem[i].p;
Vector p = l[i].p;
double d = (p.Minus(sc->xminPt)).MagSquared();
if(d < dmin) {
dmin = d;
@ -140,7 +140,7 @@ bool SContour::BridgeToContour(SContour *sc,
// merge them there, without a bridge.
for(i = 0; i < l.n; i++) {
thisp = WRAP(i+thiso, l.n);
a = l.elem[thisp].p;
a = l[thisp].p;
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
if(f->Equals(a)) break;
@ -149,7 +149,7 @@ bool SContour::BridgeToContour(SContour *sc,
for(j = 0; j < (sc->l.n - 1); j++) {
scp = WRAP(j+sco, (sc->l.n - 1));
b = sc->l.elem[scp].p;
b = sc->l[scp].p;
if(a.Equals(b)) {
goto haveEdge;
@ -160,7 +160,7 @@ bool SContour::BridgeToContour(SContour *sc,
// If that fails, look for a bridge that does not intersect any edges.
for(i = 0; i < l.n; i++) {
thisp = WRAP(i+thiso, l.n);
a = l.elem[thisp].p;
a = l[thisp].p;
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
if(f->Equals(a)) break;
@ -169,7 +169,7 @@ bool SContour::BridgeToContour(SContour *sc,
for(j = 0; j < (sc->l.n - 1); j++) {
scp = WRAP(j+sco, (sc->l.n - 1));
b = sc->l.elem[scp].p;
b = sc->l[scp].p;
for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) {
if(f->Equals(b)) break;
@ -190,15 +190,15 @@ bool SContour::BridgeToContour(SContour *sc,
haveEdge:
SContour merged = {};
for(i = 0; i < l.n; i++) {
merged.AddPoint(l.elem[i].p);
merged.AddPoint(l[i].p);
if(i == thisp) {
// less than or equal; need to duplicate the join point
for(j = 0; j <= (sc->l.n - 1); j++) {
int jp = WRAP(j + scp, (sc->l.n - 1));
merged.AddPoint((sc->l.elem[jp]).p);
merged.AddPoint((sc->l[jp]).p);
}
// and likewise duplicate join point for the outer curve
merged.AddPoint(l.elem[i].p);
merged.AddPoint(l[i].p);
}
}
@ -218,9 +218,9 @@ bool SContour::IsEar(int bp, double scaledEps) const {
cp = WRAP(bp+1, l.n);
STriangle tr = {};
tr.a = l.elem[ap].p;
tr.b = l.elem[bp].p;
tr.c = l.elem[cp].p;
tr.a = l[ap].p;
tr.b = l[bp].p;
tr.c = l[cp].p;
if((tr.a).Equals(tr.c)) {
// This is two coincident and anti-parallel edges. Zero-area, so
@ -244,7 +244,7 @@ bool SContour::IsEar(int bp, double scaledEps) const {
for(i = 0; i < l.n; i++) {
if(i == ap || i == bp || i == cp) continue;
Vector p = l.elem[i].p;
Vector p = l[i].p;
if(p.OutsideAndNotOn(maxv, minv)) continue;
// A point on the edge of the triangle is considered to be inside,
@ -266,9 +266,9 @@ void SContour::ClipEarInto(SMesh *m, int bp, double scaledEps) {
cp = WRAP(bp+1, l.n);
STriangle tr = {};
tr.a = l.elem[ap].p;
tr.b = l.elem[bp].p;
tr.c = l.elem[cp].p;
tr.a = l[ap].p;
tr.b = l[bp].p;
tr.c = l[cp].p;
if(tr.Normal().MagSquared() < scaledEps*scaledEps) {
// A vertex with more than two edges will cause us to generate
// zero-area triangles, which must be culled.
@ -278,11 +278,11 @@ void SContour::ClipEarInto(SMesh *m, int bp, double scaledEps) {
// By deleting the point at bp, we may change the ear-ness of the points
// on either side.
l.elem[ap].ear = EarType::UNKNOWN;
l.elem[cp].ear = EarType::UNKNOWN;
l[ap].ear = EarType::UNKNOWN;
l[cp].ear = EarType::UNKNOWN;
l.ClearTags();
l.elem[bp].tag = 1;
l[bp].tag = 1;
l.RemoveTagged();
}
@ -299,23 +299,23 @@ void SContour::UvTriangulateInto(SMesh *m, SSurface *srf) {
// Clean the original contour by removing any zero-length edges.
l.ClearTags();
for(i = 1; i < l.n; i++) {
if((l.elem[i].p).Equals(l.elem[i-1].p)) {
l.elem[i].tag = 1;
if((l[i].p).Equals(l[i-1].p)) {
l[i].tag = 1;
}
}
l.RemoveTagged();
// Now calculate the ear-ness of each vertex
for(i = 0; i < l.n; i++) {
(l.elem[i]).ear = IsEar(i, scaledEps) ? EarType::EAR : EarType::NOT_EAR;
(l[i]).ear = IsEar(i, scaledEps) ? EarType::EAR : EarType::NOT_EAR;
}
bool toggle = false;
while(l.n > 3) {
// Some points may have changed ear-ness, so recalculate
for(i = 0; i < l.n; i++) {
if(l.elem[i].ear == EarType::UNKNOWN) {
(l.elem[i]).ear = IsEar(i, scaledEps) ?
if(l[i].ear == EarType::UNKNOWN) {
(l[i]).ear = IsEar(i, scaledEps) ?
EarType::EAR : EarType::NOT_EAR;
}
}
@ -328,7 +328,7 @@ void SContour::UvTriangulateInto(SMesh *m, SSurface *srf) {
int offset = toggle ? -1 : 0;
for(i = 0; i < l.n; i++) {
int ear = WRAP(i+offset, l.n);
if(l.elem[ear].ear == EarType::EAR) {
if(l[ear].ear == EarType::EAR) {
if(srf->degm == 1 && srf->degn == 1) {
// This is a plane; any ear is a good ear.
bestEar = ear;
@ -337,8 +337,8 @@ void SContour::UvTriangulateInto(SMesh *m, SSurface *srf) {
// If we are triangulating a curved surface, then try to
// clip ears that have a small chord tolerance from the
// surface.
Vector prev = l.elem[WRAP((i+offset-1), l.n)].p,
next = l.elem[WRAP((i+offset+1), l.n)].p;
Vector prev = l[WRAP((i+offset-1), l.n)].p,
next = l[WRAP((i+offset+1), l.n)].p;
double tol = srf->ChordToleranceForEdge(prev, next);
if(tol < bestChordTol - scaledEps) {
bestEar = ear;
@ -444,8 +444,8 @@ void SPolygon::UvGridTriangulateInto(SMesh *mesh, SSurface *srf) {
int i, j;
for(i = 0; i < (li.n - 1); i++) {
for(j = 0; j < (lj.n - 1); j++) {
double us = li.elem[i], uf = li.elem[i+1],
vs = lj.elem[j], vf = lj.elem[j+1];
double us = li[i], uf = li[i+1],
vs = lj[j], vf = lj[j+1];
Vector a = Vector::From(us, vs, 0),
b = Vector::From(us, vf, 0),

View File

@ -59,7 +59,7 @@ void Style::CreateDefaultStyle(hStyle h) {
bool isDefaultStyle = true;
const Default *d;
for(d = &(Defaults[0]); d->h.v; d++) {
if(d->h.v == h.v) break;
if(d->h == h) break;
}
if(!d->h.v) {
// Not a default style; so just create it the same as our default
@ -337,7 +337,7 @@ hStyle Style::ForEntity(hEntity he) {
// Otherwise, we use the default rules.
hStyle hs;
if(e->group.v != SS.GW.activeGroup.v) {
if(e->group != SS.GW.activeGroup) {
hs.v = INACTIVE_GRP;
} else if(e->construction) {
hs.v = CONSTRUCTION;

View File

@ -20,28 +20,29 @@ const double System::RANK_MAG_TOLERANCE = 1e-4;
const double System::CONVERGE_TOLERANCE = (LENGTH_EPS/(1e2));
bool System::WriteJacobian(int tag) {
int a, i, j;
j = 0;
for(a = 0; a < param.n; a++) {
if(j >= MAX_UNKNOWNS) return false;
int j = 0;
for(auto &p : param) {
if(j >= MAX_UNKNOWNS)
return false;
Param *p = &(param.elem[a]);
if(p->tag != tag) continue;
mat.param[j] = p->h;
if(p.tag != tag)
continue;
mat.param[j] = p.h;
j++;
}
mat.n = j;
i = 0;
for(a = 0; a < eq.n; a++) {
int i = 0;
for(auto &e : eq) {
if(i >= MAX_UNKNOWNS) return false;
Equation *e = &(eq.elem[a]);
if(e->tag != tag) continue;
if(e.tag != tag)
continue;
mat.eq[i] = e->h;
Expr *f = e->e->DeepCopyWithParamsAsPointers(&param, &(SK.param));
mat.eq[i] = e.h;
Expr *f = e.e->DeepCopyWithParamsAsPointers(&param, &(SK.param));
f = f->FoldConstants();
// Hash table (61 bits) to accelerate generation of zero partials.
@ -79,16 +80,14 @@ void System::EvalJacobian() {
bool System::IsDragged(hParam p) {
hParam *pp;
for(pp = dragged.First(); pp; pp = dragged.NextAfter(pp)) {
if(p.v == pp->v) return true;
if(p == *pp) return true;
}
return false;
}
void System::SolveBySubstitution() {
int i;
for(i = 0; i < eq.n; i++) {
Equation *teq = &(eq.elem[i]);
Expr *tex = teq->e;
for(auto &teq : eq) {
Expr *tex = teq.e;
if(tex->op == Expr::Op::MINUS &&
tex->a->op == Expr::Op::PARAM &&
@ -108,22 +107,19 @@ void System::SolveBySubstitution() {
std::swap(a, b);
}
int j;
for(j = 0; j < eq.n; j++) {
Equation *req = &(eq.elem[j]);
(req->e)->Substitute(a, b); // A becomes B, B unchanged
for(auto &req : eq) {
req.e->Substitute(a, b); // A becomes B, B unchanged
}
for(j = 0; j < param.n; j++) {
Param *rp = &(param.elem[j]);
if(rp->substd.v == a.v) {
rp->substd = b;
for(auto &rp : param) {
if(rp.substd == a) {
rp.substd = b;
}
}
Param *ptr = param.FindById(a);
ptr->tag = VAR_SUBSTITUTED;
ptr->substd = b;
teq->tag = EQ_SUBSTITUTED;
teq.tag = EQ_SUBSTITUTED;
}
}
}
@ -324,12 +320,11 @@ bool System::NewtonSolve(int tag) {
}
void System::WriteEquationsExceptFor(hConstraint hc, Group *g) {
int i;
// Generate all the equations from constraints in this group
for(i = 0; i < SK.constraint.n; i++) {
ConstraintBase *c = &(SK.constraint.elem[i]);
if(c->group.v != g->h.v) continue;
if(c->h.v == hc.v) continue;
for(auto &con : SK.constraint) {
ConstraintBase *c = &con;
if(c->group != g->h) continue;
if(c->h == hc) continue;
if(c->HasLabel() && c->type != Constraint::Type::COMMENT &&
g->allDimsReference)
@ -349,9 +344,9 @@ void System::WriteEquationsExceptFor(hConstraint hc, Group *g) {
c->GenerateEquations(&eq);
}
// And the equations from entities
for(i = 0; i < SK.entity.n; i++) {
EntityBase *e = &(SK.entity.elem[i]);
if(e->group.v != g->h.v) continue;
for(auto &ent : SK.entity) {
EntityBase *e = &ent;
if(e->group != g->h) continue;
e->GenerateEquations(&eq);
}
@ -360,12 +355,12 @@ void System::WriteEquationsExceptFor(hConstraint hc, Group *g) {
}
void System::FindWhichToRemoveToFixJacobian(Group *g, List<hConstraint> *bad, bool forceDofCheck) {
int a, i;
int a;
for(a = 0; a < 2; a++) {
for(i = 0; i < SK.constraint.n; i++) {
ConstraintBase *c = &(SK.constraint.elem[i]);
if(c->group.v != g->h.v) continue;
for(auto &con : SK.constraint) {
ConstraintBase *c = &con;
if(c->group != g->h) continue;
if((c->type == Constraint::Type::POINTS_COINCIDENT && a == 0) ||
(c->type != Constraint::Type::POINTS_COINCIDENT && a == 1))
{
@ -409,11 +404,11 @@ SolveResult System::Solve(Group *g, int *rank, int *dof, List<hConstraint> *bad,
/*
dbp("%d equations", eq.n);
for(i = 0; i < eq.n; i++) {
dbp(" %.3f = %s = 0", eq.elem[i].e->Eval(), eq.elem[i].e->Print());
dbp(" %.3f = %s = 0", eq[i].e->Eval(), eq[i].e->Print());
}
dbp("%d parameters", param.n);
for(i = 0; i < param.n; i++) {
dbp(" param %08x at %.3f", param.elem[i].h.v, param.elem[i].val);
dbp(" param %08x at %.3f", param[i].h.v, param[i].val);
} */
// All params and equations are assigned to group zero.
@ -431,18 +426,18 @@ SolveResult System::Solve(Group *g, int *rank, int *dof, List<hConstraint> *bad,
// the system is consistent yet, but if it isn't then we'll catch that
// later.
int alone = 1;
for(i = 0; i < eq.n; i++) {
Equation *e = &(eq.elem[i]);
if(e->tag != 0) continue;
for(auto &e : eq) {
if(e.tag != 0)
continue;
hParam hp = e->e->ReferencedParams(&param);
if(hp.v == Expr::NO_PARAMS.v) continue;
if(hp.v == Expr::MULTIPLE_PARAMS.v) continue;
hParam hp = e.e->ReferencedParams(&param);
if(hp == Expr::NO_PARAMS) continue;
if(hp == Expr::MULTIPLE_PARAMS) continue;
Param *p = param.FindById(hp);
if(p->tag != 0) continue; // let rank test catch inconsistency
e->tag = alone;
e.tag = alone;
p->tag = alone;
WriteJacobian(alone);
if(!NewtonSolve(alone)) {
@ -480,23 +475,23 @@ SolveResult System::Solve(Group *g, int *rank, int *dof, List<hConstraint> *bad,
}
// System solved correctly, so write the new values back in to the
// main parameter table.
for(i = 0; i < param.n; i++) {
Param *p = &(param.elem[i]);
for(auto &p : param) {
double val;
if(p->tag == VAR_SUBSTITUTED) {
val = param.FindById(p->substd)->val;
if(p.tag == VAR_SUBSTITUTED) {
val = param.FindById(p.substd)->val;
} else {
val = p->val;
val = p.val;
}
Param *pp = SK.GetParam(p->h);
Param *pp = SK.GetParam(p.h);
pp->val = val;
pp->known = true;
pp->free = p->free;
pp->free = p.free;
}
return rankOk ? SolveResult::OKAY : SolveResult::REDUNDANT_OKAY;
didnt_converge:
SK.constraint.ClearTags();
// Not using range-for here because index is used in additional ways
for(i = 0; i < eq.n; i++) {
if(ffabs(mat.B.num[i]) > CONVERGE_TOLERANCE || isnan(mat.B.num[i])) {
// This constraint is unsatisfied.
@ -553,20 +548,19 @@ void System::MarkParamsFree(bool find) {
// If requested, find all the free (unbound) variables. This might be
// more than the number of degrees of freedom. Don't always do this,
// because the display would get annoying and it's slow.
for(int i = 0; i < param.n; i++) {
Param *p = &(param.elem[i]);
p->free = false;
for(auto &p : param) {
p.free = false;
if(find) {
if(p->tag == 0) {
p->tag = VAR_DOF_TEST;
if(p.tag == 0) {
p.tag = VAR_DOF_TEST;
WriteJacobian(0);
EvalJacobian();
int rank = CalculateRank();
if(rank == mat.m) {
p->free = true;
p.free = true;
}
p->tag = 0;
p.tag = 0;
}
}
}

View File

@ -57,8 +57,9 @@ void TextWindow::ScreenToggleGroupShown(int link, uint32_t v) {
}
void TextWindow::ScreenShowGroupsSpecial(int link, uint32_t v) {
bool state = link == 's';
for(int i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
for(hGroup hg : SK.groupOrder) {
Group *g = SK.GetGroup(hg);
g->visible = state;
}
SS.GW.persistentDirty = true;
@ -71,7 +72,7 @@ void TextWindow::ScreenActivateGroup(int link, uint32_t v) {
void TextWindow::ReportHowGroupSolved(hGroup hg) {
SS.GW.ClearSuper();
SS.TW.GoToScreen(Screen::GROUP_SOLVE_INFO);
SS.TW.shown.group.v = hg.v;
SS.TW.shown.group = hg;
SS.ScheduleShowTW();
}
void TextWindow::ScreenHowGroupSolved(int link, uint32_t v) {
@ -98,12 +99,13 @@ void TextWindow::ShowListOfGroups() {
Printf(true, "%Ft active");
Printf(false, "%Ft shown dof group-name%E");
int i;
bool afterActive = false;
for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
bool backgroundParity = false;
for(hGroup hg : SK.groupOrder) {
Group *g = SK.GetGroup(hg);
std::string s = g->DescriptionString();
bool active = (g->h.v == SS.GW.activeGroup.v);
bool active = (g->h == SS.GW.activeGroup);
bool shown = g->visible;
bool ok = g->IsSolvedOkay();
bool warn = (g->type == Group::Type::DRAWING_WORKPLANE &&
@ -120,31 +122,33 @@ void TextWindow::ShowListOfGroups() {
sprintf(sdof, "%-3d", dof);
}
}
bool ref = (g->h.v == Group::HGROUP_REFERENCES.v);
Printf(false, "%Bp%Fd "
bool ref = (g->h == Group::HGROUP_REFERENCES);
Printf(false,
"%Bp%Fd "
"%Ft%s%Fb%D%f%Ll%s%E "
"%Fb%s%D%f%Ll%s%E "
"%Fp%D%f%s%Ll%s%E "
"%Fl%Ll%D%f%s",
// Alternate between light and dark backgrounds, for readability
(i & 1) ? 'd' : 'a',
// Link that activates the group
ref ? " " : "",
g->h.v, (&TextWindow::ScreenActivateGroup),
ref ? "" : (active ? radioTrue : radioFalse),
// Link that hides or shows the group
afterActive ? " - " : "",
g->h.v, (&TextWindow::ScreenToggleGroupShown),
afterActive ? "" : (shown ? checkTrue : checkFalse),
// Link to the errors, if a problem occurred while solving
ok ? (warn ? 'm' : (dof > 0 ? 'i' : 's')) : 'x',
g->h.v, (&TextWindow::ScreenHowGroupSolved),
ok ? (warn ? "err" : sdof) : "",
ok ? "" : "ERR",
// Link to a screen that gives more details on the group
g->h.v, (&TextWindow::ScreenSelectGroup), s.c_str());
// Alternate between light and dark backgrounds, for readability
backgroundParity ? 'd' : 'a',
// Link that activates the group
ref ? " " : "",
g->h.v, (&TextWindow::ScreenActivateGroup),
ref ? "" : (active ? radioTrue : radioFalse),
// Link that hides or shows the group
afterActive ? " - " : "",
g->h.v, (&TextWindow::ScreenToggleGroupShown),
afterActive ? "" : (shown ? checkTrue : checkFalse),
// Link to the errors, if a problem occurred while solving
ok ? (warn ? 'm' : (dof > 0 ? 'i' : 's')) : 'x',
g->h.v, (&TextWindow::ScreenHowGroupSolved),
ok ? (warn ? "err" : sdof) : "",
ok ? "" : "ERR",
// Link to a screen that gives more details on the group
g->h.v, (&TextWindow::ScreenSelectGroup), s.c_str());
if(active) afterActive = true;
backgroundParity = !backgroundParity;
}
Printf(true, " %Fl%Ls%fshow all%E / %Fl%Lh%fhide all%E",
@ -245,7 +249,7 @@ void TextWindow::ScreenOpacity(int link, uint32_t v) {
SS.TW.ShowEditControl(11, ssprintf("%.2f", g->color.alphaF()));
SS.TW.edit.meaning = Edit::GROUP_OPACITY;
SS.TW.edit.group.v = g->h.v;
SS.TW.edit.group = g->h;
}
void TextWindow::ScreenChangeExprA(int link, uint32_t v) {
Group *g = SK.GetGroup(SS.TW.shown.group);
@ -271,7 +275,7 @@ void TextWindow::ScreenDeleteGroup(int link, uint32_t v) {
SS.UndoRemember();
hGroup hg = SS.TW.shown.group;
if(hg.v == SS.GW.activeGroup.v) {
if(hg == SS.GW.activeGroup) {
SS.GW.activeGroup = SK.GetGroup(SS.GW.activeGroup)->PreviousGroup()->h;
}
@ -291,7 +295,7 @@ void TextWindow::ShowGroupInfo() {
Group *g = SK.GetGroup(shown.group);
const char *s = "???";
if(shown.group.v == Group::HGROUP_REFERENCES.v) {
if(shown.group == Group::HGROUP_REFERENCES) {
Printf(true, "%FtGROUP %E%s", g->DescriptionString().c_str());
goto list_items;
} else {
@ -304,14 +308,18 @@ void TextWindow::ShowGroupInfo() {
if(g->type == Group::Type::LATHE) {
Printf(true, " %Ftlathe plane sketch");
} else if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::ROTATE ||
g->type == Group::Type::TRANSLATE)
{
g->type == Group::Type::TRANSLATE || g->type == Group::Type::REVOLVE ||
g->type == Group::Type::HELIX) {
if(g->type == Group::Type::EXTRUDE) {
s = "extrude plane sketch";
} else if(g->type == Group::Type::TRANSLATE) {
s = "translate original sketch";
} else if(g->type == Group::Type::HELIX) {
s = "create helical extrusion";
} else if(g->type == Group::Type::ROTATE) {
s = "rotate original sketch";
} else if(g->type == Group::Type::REVOLVE) {
s = "revolve original sketch";
}
Printf(true, " %Ft%s%E", s);
@ -362,10 +370,9 @@ void TextWindow::ShowGroupInfo() {
}
Printf(false, "");
if(g->type == Group::Type::EXTRUDE ||
g->type == Group::Type::LATHE ||
g->type == Group::Type::LINKED)
{
if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::LATHE ||
g->type == Group::Type::REVOLVE || g->type == Group::Type::LINKED ||
g->type == Group::Type::HELIX) {
bool un = (g->meshCombine == Group::CombineAs::UNION);
bool diff = (g->meshCombine == Group::CombineAs::DIFFERENCE);
bool asy = (g->meshCombine == Group::CombineAs::ASSEMBLE);
@ -384,9 +391,8 @@ void TextWindow::ShowGroupInfo() {
Group::CombineAs::ASSEMBLE,
(asy ? RADIO_TRUE : RADIO_FALSE));
if(g->type == Group::Type::EXTRUDE ||
g->type == Group::Type::LATHE)
{
if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::LATHE ||
g->type == Group::Type::REVOLVE || g->type == Group::Type::HELIX) {
Printf(false,
"%Bd %Ftcolor %E%Bz %Bd (%@, %@, %@) %f%D%Lf%Fl[change]%E",
&g->color,
@ -397,9 +403,9 @@ void TextWindow::ShowGroupInfo() {
&TextWindow::ScreenOpacity);
}
if(g->type == Group::Type::EXTRUDE ||
g->type == Group::Type::LATHE ||
g->type == Group::Type::LINKED) {
if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::LATHE ||
g->type == Group::Type::REVOLVE || g->type == Group::Type::LINKED ||
g->type == Group::Type::HELIX) {
Printf(false, " %Fd%f%LP%s suppress this group's solid model",
&TextWindow::ScreenChangeGroupOption,
g->suppress ? CHECK_TRUE : CHECK_FALSE);
@ -443,16 +449,17 @@ list_items:
Printf(false, "");
Printf(false, "%Ft requests in group");
int i, a = 0;
for(i = 0; i < SK.request.n; i++) {
Request *r = &(SK.request.elem[i]);
int a = 0;
for(auto &r : SK.request) {
if(r->group.v == shown.group.v) {
std::string s = r->DescriptionString();
if(r.group == shown.group) {
std::string s = r.DescriptionString();
Printf(false, "%Bp %Fl%Ll%D%f%h%s%E",
(a & 1) ? 'd' : 'a',
r->h.v, (&TextWindow::ScreenSelectRequest),
&(TextWindow::ScreenHoverRequest), s.c_str());
(a & 1) ? 'd' : 'a',
r.h.v,
(&TextWindow::ScreenSelectRequest),
&(TextWindow::ScreenHoverRequest),
s.c_str());
a++;
}
}
@ -461,16 +468,17 @@ list_items:
a = 0;
Printf(false, "");
Printf(false, "%Ft constraints in group (%d DOF)", g->solved.dof);
for(i = 0; i < SK.constraint.n; i++) {
Constraint *c = &(SK.constraint.elem[i]);
for(auto &c : SK.constraint) {
if(c->group.v == shown.group.v) {
std::string s = c->DescriptionString();
if(c.group == shown.group) {
std::string s = c.DescriptionString();
Printf(false, "%Bp %Fl%Ll%D%f%h%s%E %s",
(a & 1) ? 'd' : 'a',
c->h.v, (&TextWindow::ScreenSelectConstraint),
(&TextWindow::ScreenHoverConstraint), s.c_str(),
c->reference ? "(ref)" : "");
(a & 1) ? 'd' : 'a',
c.h.v,
(&TextWindow::ScreenSelectConstraint),
(&TextWindow::ScreenHoverConstraint),
s.c_str(),
c.reference ? "(ref)" : "");
a++;
}
}
@ -526,7 +534,7 @@ void TextWindow::ShowGroupSolveInfo() {
}
for(int i = 0; i < g->solved.remove.n; i++) {
hConstraint hc = g->solved.remove.elem[i];
hConstraint hc = g->solved.remove[i];
Constraint *c = SK.constraint.FindByIdNoOops(hc);
if(!c) continue;
@ -750,7 +758,7 @@ void TextWindow::EditControlDone(std::string s) {
Group *g = SK.group.FindByIdNoOops(SS.TW.shown.group);
if(!g) break;
g->color = RGBf(rgb.x, rgb.y, rgb.z);
g->color = RgbaColor::FromFloat(rgb.x, rgb.y, rgb.z, g->color.alphaF());
SS.MarkGroupDirty(g->h);
SS.GW.ClearSuper();

View File

@ -195,8 +195,8 @@ static Button *buttons[] = {
/** Foreground color codes. */
const TextWindow::Color TextWindow::fgColors[] = {
{ 'd', RGBi(255, 255, 255) }, // Default : white
{ 'l', RGBi(100, 100, 255) },
{ 't', RGBi(255, 200, 0) },
{ 'l', RGBi(100, 200, 255) }, // links : blue
{ 't', RGBi(255, 200, 100) }, // tree/text : yellow
{ 'h', RGBi( 90, 90, 90) },
{ 's', RGBi( 40, 255, 40) }, // Ok : green
{ 'm', RGBi(200, 200, 0) },

View File

@ -151,6 +151,7 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my, UiCanvas *canvas,
int x = 17, y = (int)(height - 52);
// When changing these values, also change the asReference drawing code in drawentity.cpp.
int fudge = 8;
int h = 34*16 + 3*16 + fudge;
int aleft = 0, aright = 66, atop = y+16+fudge/2, abot = y+16-h;
@ -174,7 +175,7 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my, UiCanvas *canvas,
bool leftpos = true;
for(ToolIcon &icon : Toolbar) {
if(icon.name == "") { // spacer
if(icon.name.empty()) { // spacer
if(!leftpos) {
leftpos = true;
y -= 32;

View File

@ -72,17 +72,17 @@ void TtfFontList::LoadAll() {
}
// Sort fonts according to their actual name, not filename.
std::sort(&l.elem[0], &l.elem[l.n],
std::sort(l.begin(), l.end(),
[](const TtfFont &a, const TtfFont &b) { return a.name < b.name; });
// Filter out fonts with the same family and style name. This is not
// strictly necessarily the exact same font, but it will almost always be.
TtfFont *it = std::unique(&l.elem[0], &l.elem[l.n],
[](const TtfFont &a, const TtfFont &b) { return a.name == b.name; });
l.RemoveLast(&l.elem[l.n] - it);
TtfFont *it = std::unique(l.begin(), l.end(),
[](const TtfFont &a, const TtfFont &b) { return a.name == b.name; });
l.RemoveLast(&l[l.n] - it);
// TODO: identify fonts by their name and not filename, which may change
// between OSes.
//! @todo identify fonts by their name and not filename, which may change
//! between OSes.
loaded = true;
}

View File

@ -125,7 +125,9 @@ enum class Command : uint32_t {
GROUP_3D,
GROUP_WRKPL,
GROUP_EXTRUDE,
GROUP_HELIX,
GROUP_LATHE,
GROUP_REVOLVE,
GROUP_ROT,
GROUP_TRANS,
GROUP_LINK,

View File

@ -36,8 +36,6 @@ void SolveSpaceUI::UndoEnableMenus() {
}
void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) {
int i;
if(uk->cnt == MAX_UNDO) {
UndoClearState(&(uk->d[uk->write]));
// And then write in to this one again
@ -48,9 +46,9 @@ void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) {
UndoState *ut = &(uk->d[uk->write]);
*ut = {};
ut->group.ReserveMore(SK.group.n);
for(i = 0; i < SK.group.n; i++) {
Group *src = &(SK.group.elem[i]);
Group dest = *src;
for(Group &src : SK.group) {
// Shallow copy
Group dest(src);
// And then clean up all the stuff that needs to be a deep copy,
// and zero out all the dynamic stuff that will get regenerated.
dest.clean = false;
@ -66,42 +64,32 @@ void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) {
dest.displayMesh = {};
dest.displayOutlines = {};
dest.remap = src->remap;
dest.remap = src.remap;
dest.impMesh = {};
dest.impShell = {};
dest.impEntity = {};
ut->group.Add(&dest);
}
for(i = 0; i < SK.groupOrder.n; i++) {
ut->groupOrder.Add(&(SK.groupOrder.elem[i]));
}
for(auto &src : SK.groupOrder) { ut->groupOrder.Add(&src); }
ut->request.ReserveMore(SK.request.n);
for(i = 0; i < SK.request.n; i++) {
ut->request.Add(&(SK.request.elem[i]));
}
for(auto &src : SK.request) { ut->request.Add(&src); }
ut->constraint.ReserveMore(SK.constraint.n);
for(i = 0; i < SK.constraint.n; i++) {
Constraint *src = &(SK.constraint.elem[i]);
Constraint dest = *src;
for(auto &src : SK.constraint) {
// Shallow copy
Constraint dest(src);
ut->constraint.Add(&dest);
}
ut->param.ReserveMore(SK.param.n);
for(i = 0; i < SK.param.n; i++) {
ut->param.Add(&(SK.param.elem[i]));
}
for(auto &src : SK.param) { ut->param.Add(&src); }
ut->style.ReserveMore(SK.style.n);
for(i = 0; i < SK.style.n; i++) {
ut->style.Add(&(SK.style.elem[i]));
}
for(auto &src : SK.style) { ut->style.Add(&src); }
ut->activeGroup = SS.GW.activeGroup;
uk->write = WRAP(uk->write + 1, MAX_UNDO);
}
void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) {
int i;
ssassert(uk->cnt > 0, "Cannot pop from empty undo stack");
(uk->cnt)--;
uk->write = WRAP(uk->write - 1, MAX_UNDO);
@ -109,8 +97,8 @@ void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) {
UndoState *ut = &(uk->d[uk->write]);
// Free everything in the main copy of the program before replacing it
for(i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
for(hGroup hg : SK.groupOrder) {
Group *g = SK.GetGroup(hg);
g->Clear();
}
SK.group.Clear();
@ -122,8 +110,7 @@ void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) {
// And then do a shallow copy of the state from the undo list
ut->group.MoveSelfInto(&(SK.group));
for(i = 0; i < ut->groupOrder.n; i++)
SK.groupOrder.Add(&ut->groupOrder.elem[i]);
for(auto &gh : ut->groupOrder) { SK.groupOrder.Add(&gh); }
ut->request.MoveSelfInto(&(SK.request));
ut->constraint.MoveSelfInto(&(SK.constraint));
ut->param.MoveSelfInto(&(SK.param));
@ -156,12 +143,7 @@ void SolveSpaceUI::UndoClearStack(UndoStack *uk) {
}
void SolveSpaceUI::UndoClearState(UndoState *ut) {
int i;
for(i = 0; i < ut->group.n; i++) {
Group *g = &(ut->group.elem[i]);
g->remap.clear();
}
for(auto &g : ut->group) { g.remap.clear(); }
ut->group.Clear();
ut->request.Clear();
ut->constraint.Clear();

View File

@ -143,7 +143,17 @@ static void MessageBox(const char *fmt, va_list va, bool error,
description = description.substr(it - description.begin());
Platform::MessageDialogRef dialog = CreateMessageDialog(SS.GW.window);
if (!dialog) {
if (error) {
fprintf(stderr, "Error: %s\n", message.c_str());
} else {
fprintf(stderr, "Message: %s\n", message.c_str());
}
if(onDismiss) {
onDismiss();
}
return;
}
using Platform::MessageDialog;
if(error) {
dialog->SetType(MessageDialog::Type::ERROR);
@ -597,7 +607,7 @@ bool Vector::OnLineSegment(Vector a, Vector b, double tol) const {
if(distsq >= tol*tol) return false;
double t = (this->Minus(a)).DivPivoting(d);
double t = (this->Minus(a)).DivProjected(d);
// On-endpoint already tested
if(t < 0 || t > 1) return false;
return true;
@ -686,16 +696,9 @@ Vector4 Vector::Project4d() const {
return Vector4::From(1, x, y, z);
}
double Vector::DivPivoting(Vector delta) const {
double mx = fabs(delta.x), my = fabs(delta.y), mz = fabs(delta.z);
if(mx > my && mx > mz) {
return x/delta.x;
} else if(my > mz) {
return y/delta.y;
} else {
return z/delta.z;
}
double Vector::DivProjected(Vector delta) const {
return (x*delta.x + y*delta.y + z*delta.z)
/ (delta.x*delta.x + delta.y*delta.y + delta.z*delta.z);
}
Vector Vector::ClosestOrtho() const {
@ -985,12 +988,8 @@ Point2d Point2d::ScaledBy(double s) const {
return { x * s, y * s };
}
double Point2d::DivPivoting(Point2d delta) const {
if(fabs(delta.x) > fabs(delta.y)) {
return x/delta.x;
} else {
return y/delta.y;
}
double Point2d::DivProjected(Point2d delta) const {
return (x*delta.x + y*delta.y) / (delta.x*delta.x + delta.y*delta.y);
}
double Point2d::MagSquared() const {

View File

@ -363,6 +363,7 @@ int main(int argc, char **argv) {
}
SS.Init();
SS.showToolbar = false;
SS.checkClosedContour = false;
Test::Helper helper = {};